Browse Source

Completely removed control page and all it's related code

maziggy 5 months ago
parent
commit
06bfaa3c74
63 changed files with 16 additions and 34725 deletions
  1. 0 522
      PLAN_PRINTER_CONTROL.md
  2. 0 770
      backend/app/api/routes/printer_control.py
  3. 1 2
      backend/app/main.py
  4. 11 28
      frontend/package-lock.json
  5. 0 2
      frontend/src/App.tsx
  6. 1 200
      frontend/src/api/client.ts
  7. 1 2
      frontend/src/components/Layout.tsx
  8. 0 100
      frontend/src/components/control/AMSHumidityModal.tsx
  9. 0 674
      frontend/src/components/control/AMSMaterialsModal.tsx
  10. 0 192
      frontend/src/components/control/AMSPanel.tsx
  11. 0 1326
      frontend/src/components/control/AMSSectionDual.tsx
  12. 0 451
      frontend/src/components/control/AirConditionModal.tsx
  13. 0 100
      frontend/src/components/control/BedControls.tsx
  14. 0 424
      frontend/src/components/control/CalibrationModal.tsx
  15. 0 130
      frontend/src/components/control/CameraFeed.tsx
  16. 0 167
      frontend/src/components/control/ExtruderControls.tsx
  17. 0 135
      frontend/src/components/control/FanControls.tsx
  18. 0 230
      frontend/src/components/control/JogPad.tsx
  19. 0 68
      frontend/src/components/control/LightToggle.tsx
  20. 0 235
      frontend/src/components/control/MovementControls.tsx
  21. 0 137
      frontend/src/components/control/PrintControls.tsx
  22. 0 393
      frontend/src/components/control/PrintOptionsModal.tsx
  23. 0 190
      frontend/src/components/control/PrintStatus.tsx
  24. 0 192
      frontend/src/components/control/PrinterPartsModal.tsx
  25. 0 71
      frontend/src/components/control/SpeedControl.tsx
  26. 0 133
      frontend/src/components/control/SpeedModal.tsx
  27. 0 270
      frontend/src/components/control/TemperatureColumn.tsx
  28. 0 246
      frontend/src/components/control/TemperaturePanel.tsx
  29. 0 377
      frontend/src/components/control/useAmsOperations.ts
  30. 0 1
      frontend/src/i18n/locales/de.ts
  31. 0 1
      frontend/src/i18n/locales/en.ts
  32. 0 294
      frontend/src/pages/ControlPage.tsx
  33. 0 1026
      mockup/control-page-mockup.html
  34. 0 923
      mockup/control-page-v10.html
  35. 0 936
      mockup/control-page-v11.html
  36. 0 1174
      mockup/control-page-v12.html
  37. 0 1138
      mockup/control-page-v13.html
  38. 0 1055
      mockup/control-page-v14.html
  39. 0 1091
      mockup/control-page-v15.html
  40. 0 1206
      mockup/control-page-v16.html
  41. 0 1032
      mockup/control-page-v17.html
  42. 0 1061
      mockup/control-page-v18.html
  43. 0 1061
      mockup/control-page-v19.html
  44. 0 1123
      mockup/control-page-v2.html
  45. 0 1131
      mockup/control-page-v20.html
  46. 0 1108
      mockup/control-page-v21.html
  47. 0 1119
      mockup/control-page-v22.html
  48. 0 1127
      mockup/control-page-v23.html
  49. 0 1136
      mockup/control-page-v24.html
  50. 0 1083
      mockup/control-page-v25.html
  51. 0 1082
      mockup/control-page-v3.html
  52. 0 1051
      mockup/control-page-v4.html
  53. 0 952
      mockup/control-page-v5.html
  54. 0 690
      mockup/control-page-v6-multi-ams.html
  55. 0 1125
      mockup/control-page-v6.html
  56. 0 654
      mockup/control-page-v7.html
  57. 0 652
      mockup/control-page-v8.html
  58. 0 924
      mockup/control-page-v9.html
  59. 0 0
      static/assets/index-BpSfhfce.css
  60. 0 0
      static/assets/index-CVJMI5JU.css
  61. 0 0
      static/assets/index-DRrbGWbR.js
  62. 0 0
      static/assets/index-DbzgStXX.js
  63. 2 2
      static/index.html

+ 0 - 522
PLAN_PRINTER_CONTROL.md

@@ -1,522 +0,0 @@
-# Full Printer Control - Implementation Plan
-
-## Overview
-
-Add a dedicated **Control Page** (`/control`) with full printer control capabilities, including:
-- Live camera feed
-- Print control (pause/resume/stop)
-- Temperature control (bed, nozzle, chamber)
-- Speed adjustment
-- Fan control
-- Light control
-- Axis movement
-- AMS visualization and operations
-
----
-
-## Phase 1: Backend - MQTT Control Commands
-
-### 1.1 Add Control Methods to `bambu_mqtt.py`
-
-```python
-# Print Control
-async def pause_print(self) -> bool
-async def resume_print(self) -> bool
-# stop_print() already exists
-
-# Temperature Control
-async def set_bed_temperature(self, target: int) -> bool
-async def set_nozzle_temperature(self, target: int, nozzle: int = 0) -> bool
-
-# Speed Control
-async def set_print_speed(self, mode: int) -> bool  # 1=silent, 2=standard, 3=sport, 4=ludicrous
-
-# Fan Control
-async def set_part_fan(self, speed: int) -> bool  # 0-255
-async def set_aux_fan(self, speed: int) -> bool   # 0-255
-async def set_chamber_fan(self, speed: int) -> bool  # 0-255
-
-# Light Control
-async def set_chamber_light(self, on: bool) -> bool
-
-# Movement Control
-async def home_axes(self, axes: str = "XYZ") -> bool
-async def move_axis(self, axis: str, distance: float, speed: int = 3000) -> bool
-async def disable_motors(self) -> bool
-
-# AMS Control
-async def ams_load_filament(self, tray_id: int) -> bool
-async def ams_unload_filament(self) -> bool
-
-# G-code
-async def send_gcode(self, gcode: str) -> bool
-```
-
-### 1.2 MQTT Command Formats
-
-| Command | JSON Payload |
-|---------|-------------|
-| Pause | `{"print": {"sequence_id": "0", "command": "pause"}}` |
-| Resume | `{"print": {"sequence_id": "0", "command": "resume"}}` |
-| Bed Temp | `{"print": {"sequence_id": "0", "command": "gcode_line", "param": "M140 S{temp}"}}` |
-| Nozzle Temp | `{"print": {"sequence_id": "0", "command": "gcode_line", "param": "M104 S{temp}"}}` |
-| Print Speed | `{"print": {"sequence_id": "0", "command": "print_speed", "param": "{1-4}"}}` |
-| Fan (P1=part, P2=aux, P3=chamber) | `{"print": {"sequence_id": "0", "command": "gcode_line", "param": "M106 P{n} S{0-255}"}}` |
-| Light On | `{"system": {"sequence_id": "0", "command": "ledctrl", "led_node": "chamber_light", "led_mode": "on", ...}}` |
-| Home | `{"print": {"sequence_id": "0", "command": "gcode_line", "param": "G28 {axes}"}}` |
-| Move | `{"print": {"sequence_id": "0", "command": "gcode_line", "param": "G91\nG0 {axis}{dist} F{speed}\nG90"}}` |
-| AMS Load | `{"print": {"sequence_id": "0", "command": "ams_change_filament", "target": {tray_id}}}` |
-| AMS Unload | `{"print": {"sequence_id": "0", "command": "ams_change_filament", "target": 255}}` |
-
-### 1.3 Model-Specific Handling
-
-- **P1/A1 series**: Use blocking temp commands (M109/M190) instead of M104/M140
-- **H2D**: Handle dual nozzle targeting
-- Store printer model in status for frontend to adapt UI
-
----
-
-## Phase 2: Backend - Control API Endpoints
-
-### 2.1 New Routes in `backend/app/api/routes/printer_control.py`
-
-```python
-# Print Control
-POST /api/v1/printers/{id}/control/pause
-POST /api/v1/printers/{id}/control/resume
-POST /api/v1/printers/{id}/control/stop
-
-# Temperature
-POST /api/v1/printers/{id}/control/temperature/bed
-  Body: {"target": 60}
-POST /api/v1/printers/{id}/control/temperature/nozzle
-  Body: {"target": 200, "nozzle": 0}
-
-# Speed
-POST /api/v1/printers/{id}/control/speed
-  Body: {"mode": 2}  # 1-4
-
-# Fans
-POST /api/v1/printers/{id}/control/fan/part
-  Body: {"speed": 255}  # 0-255
-POST /api/v1/printers/{id}/control/fan/aux
-POST /api/v1/printers/{id}/control/fan/chamber
-
-# Light
-POST /api/v1/printers/{id}/control/light
-  Body: {"on": true}
-
-# Movement
-POST /api/v1/printers/{id}/control/home
-  Body: {"axes": "XYZ"}  # optional, default all
-POST /api/v1/printers/{id}/control/move
-  Body: {"axis": "Z", "distance": 10, "speed": 600}
-POST /api/v1/printers/{id}/control/motors/disable
-
-# AMS
-POST /api/v1/printers/{id}/control/ams/load
-  Body: {"tray_id": 0}
-POST /api/v1/printers/{id}/control/ams/unload
-
-# G-code (advanced)
-POST /api/v1/printers/{id}/control/gcode
-  Body: {"command": "G28"}
-```
-
-### 2.2 Safety Confirmations
-
-Commands that need confirmation token (generated and validated server-side):
-- `stop` - Aborts print
-- `home` while printing - Could cause issues
-- `move` while printing - Dangerous
-- `motors/disable` - Causes position loss
-
-Flow:
-1. Frontend calls endpoint without token
-2. Backend returns `{"requires_confirmation": true, "token": "abc123", "warning": "This will abort..."}`
-3. Frontend shows confirmation dialog
-4. Frontend calls again with `{"confirm_token": "abc123"}`
-5. Backend validates token and executes
-
----
-
-## Phase 3: Backend - Camera Streaming
-
-### 3.1 Streaming Approach
-
-Option A: **MJPEG Stream** (simpler)
-- Backend captures RTSP frames via ffmpeg
-- Serves as MJPEG stream at `/api/v1/printers/{id}/camera/stream`
-- Frontend uses `<img src="...">` with streaming
-
-Option B: **WebSocket Frames** (more control)
-- Backend sends JPEG frames via WebSocket
-- Frontend renders on canvas
-- Allows frame rate control, pause/resume
-
-**Recommended: Option A (MJPEG)** - Simpler, works in all browsers
-
-### 3.2 Implementation
-
-```python
-# backend/app/api/routes/camera.py
-
-@router.get("/printers/{printer_id}/camera/stream")
-async def camera_stream(printer_id: int):
-    """Stream camera as MJPEG"""
-    printer = get_printer(printer_id)
-
-    async def generate():
-        process = await asyncio.create_subprocess_exec(
-            'ffmpeg',
-            '-rtsp_transport', 'tcp',
-            '-i', f'rtsps://bblp:{printer.access_code}@{printer.ip_address}:{port}/streaming/live/1',
-            '-f', 'mjpeg',
-            '-q:v', '5',
-            '-r', '15',  # 15 fps
-            '-',
-            stdout=asyncio.subprocess.PIPE
-        )
-
-        while True:
-            frame = await read_jpeg_frame(process.stdout)
-            if not frame:
-                break
-            yield (
-                b'--frame\r\n'
-                b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n'
-            )
-
-    return StreamingResponse(
-        generate(),
-        media_type='multipart/x-mixed-replace; boundary=frame'
-    )
-
-@router.get("/printers/{printer_id}/camera/snapshot")
-async def camera_snapshot(printer_id: int):
-    """Get single camera frame"""
-    # Use existing camera.py capture_frame logic
-```
-
-### 3.3 Camera Ports by Model
-
-| Model | Port | Protocol |
-|-------|------|----------|
-| X1/X1C/H2D | 322 | RTSPS |
-| P1/P1S/P1P | 6000 | RTSPS |
-| A1/A1 Mini | 6000 | RTSPS |
-
----
-
-## Phase 4: Frontend - Control Page
-
-### 4.1 Page Structure
-
-```
-/control
-├── ControlPage.tsx           # Main page with printer tabs
-├── components/
-│   ├── CameraFeed.tsx        # Live video stream
-│   ├── PrintControls.tsx     # Pause/Resume/Stop + progress
-│   ├── TemperaturePanel.tsx  # Bed/Nozzle/Chamber controls
-│   ├── SpeedControl.tsx      # Speed mode selector
-│   ├── FanControls.tsx       # Part/Aux/Chamber fan sliders
-│   ├── LightToggle.tsx       # Chamber light on/off
-│   ├── MovementControls.tsx  # Home + XYZ jog buttons
-│   ├── AMSPanel.tsx          # AMS visualization + load/unload
-│   └── ConfirmDialog.tsx     # Safety confirmation modal
-```
-
-### 4.2 Layout (Desktop)
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│  [Printer 1] [Printer 2] [Printer 3]                    tabs     │
-├──────────────────────────────────────────────────────────────────┤
-│                                                                  │
-│  ┌─────────────────────────┐  ┌────────────────────────────────┐│
-│  │                         │  │  Print Status                  ││
-│  │     Camera Feed         │  │  ┌────────────────────────┐    ││
-│  │     (16:9 aspect)       │  │  │ State: RUNNING         │    ││
-│  │                         │  │  │ File: benchy.3mf       │    ││
-│  │                         │  │  │ Progress: ████████░░ 78%│   ││
-│  │                         │  │  │ Layer: 156/200         │    ││
-│  │                         │  │  │ Time: 45min remaining  │    ││
-│  │                         │  │  └────────────────────────┘    ││
-│  │                         │  │                                ││
-│  │  [⏸ Pause] [■ Stop]     │  │  [⏸ Pause] [▶ Resume] [■ Stop]││
-│  └─────────────────────────┘  └────────────────────────────────┘│
-│                                                                  │
-│  ┌─────────────────────────┐  ┌────────────────────────────────┐│
-│  │  Temperatures           │  │  Speed & Fans                  ││
-│  │  ┌───────────────────┐  │  │  Speed: [Silent][Std][Sport][!]││
-│  │  │ 🛏️ Bed             │  │  │                                ││
-│  │  │ 60°C → 60°C       │  │  │  Part Fan:    ████████░░ 80%  ││
-│  │  │ [-] [target] [+]  │  │  │  Aux Fan:     ░░░░░░░░░░  0%  ││
-│  │  ├───────────────────┤  │  │  Chamber Fan: ████░░░░░░ 40%  ││
-│  │  │ 🔥 Nozzle          │  │  └────────────────────────────────┘│
-│  │  │ 205°C → 210°C     │  │                                    │
-│  │  │ [-] [target] [+]  │  │  ┌────────────────────────────────┐│
-│  │  ├───────────────────┤  │  │  Movement                      ││
-│  │  │ 📦 Chamber: 35°C   │  │  │        [Y+]                   ││
-│  │  └───────────────────┘  │  │  [X-]  [Home]  [X+]            ││
-│  └─────────────────────────┘  │        [Y-]       [Z+][Z-]     ││
-│                               │  [Disable Motors]              ││
-│  ┌─────────────────────────┐  └────────────────────────────────┘│
-│  │  💡 Light  [ON] / [OFF] │                                    │
-│  └─────────────────────────┘                                    │
-│                                                                  │
-│  ┌──────────────────────────────────────────────────────────────┐│
-│  │  AMS                                                         ││
-│  │  ┌────┐ ┌────┐ ┌────┐ ┌────┐    [Load] [Unload]             ││
-│  │  │ 1  │ │ 2  │ │ 3  │ │ 4  │                                ││
-│  │  │ 🔴 │ │ 🔵 │ │ ⚪ │ │ ⬛ │    Selected: Slot 1 (PLA Red)  ││
-│  │  │80% │ │45% │ │100%│ │ -- │                                ││
-│  │  └────┘ └────┘ └────┘ └────┘                                ││
-│  └──────────────────────────────────────────────────────────────┘│
-└──────────────────────────────────────────────────────────────────┘
-```
-
-### 4.3 Mobile Layout
-
-Stacked vertically:
-1. Camera (full width)
-2. Print controls
-3. Temperatures (collapsible)
-4. Speed/Fans (collapsible)
-5. Movement (collapsible)
-6. AMS (collapsible)
-
-### 4.4 State Management
-
-Use React Query for:
-- Printer status (already exists, real-time via WebSocket)
-- Control mutations with optimistic updates
-
-```typescript
-// Example mutation
-const pausePrint = useMutation({
-  mutationFn: (printerId: number) =>
-    api.post(`/printers/${printerId}/control/pause`),
-  onSuccess: () => {
-    // Optimistic: printer status will update via WebSocket
-  }
-});
-```
-
----
-
-## Phase 5: Component Details
-
-### 5.1 CameraFeed Component
-
-```typescript
-interface CameraFeedProps {
-  printerId: number;
-  enabled: boolean;
-}
-
-// Features:
-// - MJPEG stream from /api/v1/printers/{id}/camera/stream
-// - Fallback to static thumbnail if stream fails
-// - Loading state with skeleton
-// - Click to fullscreen
-// - Optional: snapshot button
-```
-
-### 5.2 TemperaturePanel Component
-
-```typescript
-interface TemperaturePanelProps {
-  printerId: number;
-  bed: { current: number; target: number };
-  nozzle: { current: number; target: number };
-  nozzle2?: { current: number; target: number }; // H2D
-  chamber?: number;
-}
-
-// Features:
-// - Visual temperature bars (current vs target)
-// - Input field or +/- buttons for target
-// - Presets: Off (0), PLA (60/200), PETG (70/230), ABS (90/250)
-// - Debounced API calls (don't spam on rapid clicks)
-// - Disable controls during print (optional setting)
-```
-
-### 5.3 SpeedControl Component
-
-```typescript
-// Speed modes as toggle buttons:
-// [Silent] [Standard] [Sport] [Ludicrous]
-// Visual feedback for current mode
-// Warning tooltip for Ludicrous mode
-```
-
-### 5.4 FanControls Component
-
-```typescript
-// Sliders for each fan (0-100%)
-// Convert to 0-255 for API
-// Real-time value display
-// Disable chamber fan if not available (check model)
-```
-
-### 5.5 MovementControls Component
-
-```typescript
-// Grid layout:
-//        [Y+10] [Y+1]
-// [X-10] [X-1] [Home] [X+1] [X+10]
-//        [Y-1] [Y-10]
-//                    [Z+10] [Z+1] [Z-1] [Z-10]
-//
-// [Disable Motors] button with confirmation
-// Warning: "Movement controls disabled during print" overlay
-```
-
-### 5.6 AMSPanel Component
-
-```typescript
-// Visual representation matching Bambu style:
-// - 4 slots per AMS unit
-// - Color-coded by filament
-// - Percentage remaining
-// - Active slot indicator (animated)
-// - Click to select slot
-// - [Load Selected] [Unload] buttons
-// - Support for external spool indicator
-```
-
----
-
-## Phase 6: Safety Features
-
-### 6.1 Confirmation Dialogs
-
-Required for:
-- **Stop Print**: "This will abort the current print. Are you sure?"
-- **Home During Print**: "Homing during a print is not recommended. Continue?"
-- **Move During Print**: "Manual movement during printing can damage your print. Continue?"
-- **Disable Motors**: "This will disable motors and lose position. Home before next print."
-- **High Temperatures**: Warning for temps > 260°C nozzle or > 100°C bed
-
-### 6.2 State-Based Disabling
-
-| Control | IDLE | RUNNING | PAUSE | FINISH |
-|---------|------|---------|-------|--------|
-| Pause | ❌ | ✅ | ❌ | ❌ |
-| Resume | ❌ | ❌ | ✅ | ❌ |
-| Stop | ❌ | ✅ | ✅ | ❌ |
-| Temp Control | ✅ | ⚠️ | ✅ | ✅ |
-| Speed | ❌ | ✅ | ❌ | ❌ |
-| Fans | ✅ | ⚠️ | ✅ | ✅ |
-| Movement | ✅ | ❌ | ⚠️ | ✅ |
-| AMS Load | ✅ | ❌ | ❌ | ✅ |
-
-⚠️ = Allowed with warning
-
----
-
-## Phase 7: WebSocket Updates
-
-### 7.1 Extended Status Data
-
-Ensure these fields are included in printer status broadcasts:
-
-```typescript
-interface PrinterStatus {
-  // Existing
-  state: string;
-  progress: number;
-  remaining_time: number;
-  temperatures: {...};
-
-  // Add for control page
-  print_speed_mode: number;      // 1-4
-  fan_speeds: {
-    part: number;      // 0-255
-    aux: number;
-    chamber: number;
-  };
-  light_state: boolean;
-  ams_status: {
-    units: [{
-      id: number;
-      trays: [{
-        id: number;
-        color: string;      // hex
-        type: string;       // PLA, PETG, etc
-        remaining: number;  // percentage
-        active: boolean;
-      }];
-    }];
-    current_tray: number;
-  };
-  position?: {
-    x: number;
-    y: number;
-    z: number;
-  };
-}
-```
-
----
-
-## Implementation Order
-
-1. **Backend MQTT commands** - Add all control methods
-2. **Backend API endpoints** - Create control routes with safety
-3. **Backend camera streaming** - MJPEG endpoint
-4. **Frontend ControlPage** - Basic structure with tabs
-5. **Frontend CameraFeed** - Live stream component
-6. **Frontend PrintControls** - Pause/Resume/Stop
-7. **Frontend TemperaturePanel** - Temp controls
-8. **Frontend SpeedControl** - Speed mode
-9. **Frontend FanControls** - Fan sliders
-10. **Frontend LightToggle** - Light switch
-11. **Frontend MovementControls** - Jog buttons
-12. **Frontend AMSPanel** - AMS visualization
-13. **Navigation integration** - Add to sidebar
-14. **Testing & refinement** - All printer models
-
----
-
-## Files to Create/Modify
-
-### New Files
-```
-backend/app/api/routes/printer_control.py
-backend/app/api/routes/camera.py
-backend/app/schemas/control.py
-frontend/src/pages/ControlPage.tsx
-frontend/src/components/control/CameraFeed.tsx
-frontend/src/components/control/PrintControls.tsx
-frontend/src/components/control/TemperaturePanel.tsx
-frontend/src/components/control/SpeedControl.tsx
-frontend/src/components/control/FanControls.tsx
-frontend/src/components/control/LightToggle.tsx
-frontend/src/components/control/MovementControls.tsx
-frontend/src/components/control/AMSPanel.tsx
-frontend/src/components/control/ConfirmDialog.tsx
-```
-
-### Modified Files
-```
-backend/app/services/bambu_mqtt.py     # Add control methods
-backend/app/api/routes/__init__.py     # Register new routes
-backend/app/main.py                    # Include new router
-backend/app/schemas/printer.py         # Extend status schema
-frontend/src/App.tsx                   # Add route
-frontend/src/components/Sidebar.tsx    # Add nav item
-frontend/src/api/client.ts             # Add control API calls
-```
-
----
-
-## Estimated Scope
-
-- Backend: ~500 lines new code
-- Frontend: ~1500 lines new code
-- Total: ~2000 lines
-
-Ready to begin implementation?

+ 0 - 770
backend/app/api/routes/printer_control.py

@@ -1,770 +0,0 @@
-"""Printer control API endpoints for full printer control."""
-
-import logging
-import secrets
-import time
-from typing import Optional
-
-from fastapi import APIRouter, HTTPException, Depends
-from pydantic import BaseModel, Field
-from sqlalchemy.ext.asyncio import AsyncSession
-from sqlalchemy import select
-
-from backend.app.core.database import get_db
-from backend.app.models.printer import Printer
-from backend.app.services.printer_manager import printer_manager
-
-logger = logging.getLogger(__name__)
-router = APIRouter(prefix="/printers", tags=["printer-control"])
-
-# Store confirmation tokens with expiry: {token: (printer_id, action, expiry_time)}
-_confirmation_tokens: dict[str, tuple[int, str, float]] = {}
-CONFIRMATION_TOKEN_EXPIRY = 60  # seconds
-
-
-def _clean_expired_tokens():
-    """Remove expired confirmation tokens."""
-    now = time.time()
-    expired = [t for t, (_, _, exp) in _confirmation_tokens.items() if now > exp]
-    for token in expired:
-        _confirmation_tokens.pop(token, None)
-
-
-def _create_confirmation_token(printer_id: int, action: str) -> str:
-    """Create a confirmation token for dangerous operations."""
-    _clean_expired_tokens()
-    token = secrets.token_urlsafe(16)
-    _confirmation_tokens[token] = (printer_id, action, time.time() + CONFIRMATION_TOKEN_EXPIRY)
-    return token
-
-
-def _validate_confirmation_token(token: str, printer_id: int, action: str) -> bool:
-    """Validate and consume a confirmation token."""
-    _clean_expired_tokens()
-    if token not in _confirmation_tokens:
-        return False
-    stored_printer_id, stored_action, expiry = _confirmation_tokens[token]
-    if stored_printer_id != printer_id or stored_action != action:
-        return False
-    if time.time() > expiry:
-        _confirmation_tokens.pop(token, None)
-        return False
-    # Consume the token
-    _confirmation_tokens.pop(token, None)
-    return True
-
-
-async def get_printer_or_404(printer_id: int, db: AsyncSession) -> Printer:
-    """Get printer by ID or raise 404."""
-    result = await db.execute(select(Printer).where(Printer.id == printer_id))
-    printer = result.scalar_one_or_none()
-    if not printer:
-        raise HTTPException(status_code=404, detail="Printer not found")
-    return printer
-
-
-def get_mqtt_client_or_503(printer_id: int):
-    """Get MQTT client for printer or raise 503."""
-    client = printer_manager.get_client(printer_id)
-    if not client:
-        raise HTTPException(status_code=503, detail="Printer not connected")
-    if not client.state.connected:
-        raise HTTPException(status_code=503, detail="Printer connection lost")
-    return client
-
-
-# =============================================================================
-# Request/Response Models
-# =============================================================================
-
-class ControlResponse(BaseModel):
-    success: bool
-    message: str
-
-
-class ConfirmationRequired(BaseModel):
-    requires_confirmation: bool = True
-    token: str
-    warning: str
-    expires_in: int = CONFIRMATION_TOKEN_EXPIRY
-
-
-class ConfirmableRequest(BaseModel):
-    confirm_token: Optional[str] = None
-
-
-class TemperatureRequest(ConfirmableRequest):
-    target: int = Field(..., ge=0, le=350, description="Target temperature in Celsius")
-
-
-class NozzleTemperatureRequest(TemperatureRequest):
-    nozzle: int = Field(default=0, ge=0, le=1, description="Nozzle index (0 or 1 for dual nozzle)")
-
-
-class SpeedRequest(BaseModel):
-    mode: int = Field(..., ge=1, le=4, description="Speed mode: 1=silent, 2=standard, 3=sport, 4=ludicrous")
-
-
-class ExtruderRequest(BaseModel):
-    extruder: int = Field(..., ge=0, le=1, description="Extruder index (0=right, 1=left for H2D)")
-
-
-class FanRequest(BaseModel):
-    speed: int = Field(..., ge=0, le=100, description="Fan speed percentage (0-100)")
-
-
-class LightRequest(BaseModel):
-    on: bool = Field(..., description="Light state: true=on, false=off")
-
-
-class CameraSettingRequest(BaseModel):
-    enable: bool = Field(..., description="Enable or disable the setting")
-
-
-class HomeRequest(ConfirmableRequest):
-    axes: str = Field(default="XYZ", description="Axes to home (e.g., 'XYZ', 'X', 'XY', 'Z')")
-
-
-class MoveRequest(ConfirmableRequest):
-    axis: str = Field(..., pattern="^[XYZxyz]$", description="Axis to move: X, Y, or Z")
-    distance: float = Field(..., ge=-100, le=100, description="Distance in mm (positive or negative)")
-    speed: int = Field(default=3000, ge=100, le=10000, description="Movement speed in mm/min")
-
-
-class AMSLoadRequest(BaseModel):
-    tray_id: int = Field(..., ge=0, le=254, description="Tray ID (0-15 for AMS, 254 for external)")
-    extruder_id: int | None = Field(default=None, ge=0, le=1, description="Extruder ID for dual-nozzle printers (0=right, 1=left)")
-
-
-class AMSRefreshTrayRequest(BaseModel):
-    ams_id: int = Field(..., ge=0, le=128, description="AMS unit ID (0-3, or 128 for H2D external)")
-    tray_id: int = Field(..., ge=0, le=3, description="Tray ID within the AMS (0-3)")
-
-
-class AMSFilamentSettingRequest(BaseModel):
-    ams_id: int = Field(..., ge=0, le=128, description="AMS unit ID (0-3, or 128 for H2D external)")
-    tray_id: int = Field(..., ge=0, le=3, description="Tray ID within the AMS (0-3)")
-    tray_info_idx: str = Field(..., description="Filament preset ID (e.g., 'GFA00')")
-    tray_type: str = Field(..., description="Filament type (e.g., 'PLA', 'PETG')")
-    tray_sub_brands: str = Field(default="", description="Sub-brand name (e.g., 'PLA Basic')")
-    tray_color: str = Field(..., description="Color in RRGGBBAA hex format")
-    nozzle_temp_min: int = Field(..., ge=150, le=350, description="Minimum nozzle temperature")
-    nozzle_temp_max: int = Field(..., ge=150, le=350, description="Maximum nozzle temperature")
-    k: float = Field(..., ge=0, le=1, description="Pressure advance (K) value")
-
-
-class GcodeRequest(ConfirmableRequest):
-    command: str = Field(..., min_length=1, max_length=500, description="G-code command(s)")
-
-
-# =============================================================================
-# Print Control Endpoints
-# =============================================================================
-
-@router.post("/{printer_id}/control/pause", response_model=ControlResponse)
-async def pause_print(
-    printer_id: int,
-    db: AsyncSession = Depends(get_db),
-):
-    """Pause the current print job."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Check if printer is actually printing
-    if client.state.state != "RUNNING":
-        raise HTTPException(status_code=400, detail="Printer is not currently printing")
-
-    success = client.pause_print()
-    return ControlResponse(
-        success=success,
-        message="Pause command sent" if success else "Failed to send pause command"
-    )
-
-
-@router.post("/{printer_id}/control/resume", response_model=ControlResponse)
-async def resume_print(
-    printer_id: int,
-    db: AsyncSession = Depends(get_db),
-):
-    """Resume a paused print job."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Check if printer is actually paused
-    if client.state.state != "PAUSE":
-        raise HTTPException(status_code=400, detail="Printer is not paused")
-
-    success = client.resume_print()
-    return ControlResponse(
-        success=success,
-        message="Resume command sent" if success else "Failed to send resume command"
-    )
-
-
-@router.post("/{printer_id}/control/stop")
-async def stop_print(
-    printer_id: int,
-    request: ConfirmableRequest = None,
-    db: AsyncSession = Depends(get_db),
-):
-    """Stop the current print job. Requires confirmation."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Check if printer is printing or paused
-    if client.state.state not in ("RUNNING", "PAUSE"):
-        raise HTTPException(status_code=400, detail="No active print to stop")
-
-    # Require confirmation for stop
-    if not request or not request.confirm_token:
-        token = _create_confirmation_token(printer_id, "stop")
-        return ConfirmationRequired(
-            token=token,
-            warning="This will abort the current print. The print cannot be resumed. Are you sure?"
-        )
-
-    if not _validate_confirmation_token(request.confirm_token, printer_id, "stop"):
-        raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.stop_print()
-    return ControlResponse(
-        success=success,
-        message="Stop command sent" if success else "Failed to send stop command"
-    )
-
-
-# =============================================================================
-# Temperature Control Endpoints
-# =============================================================================
-
-@router.post("/{printer_id}/control/temperature/bed", response_model=ControlResponse)
-async def set_bed_temperature(
-    printer_id: int,
-    request: TemperatureRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set the bed target temperature."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Warn for high temperatures
-    if request.target > 100 and not request.confirm_token:
-        token = _create_confirmation_token(printer_id, "bed_temp")
-        return ConfirmationRequired(
-            token=token,
-            warning=f"Setting bed to {request.target}°C is unusually high. Confirm?"
-        )
-
-    if request.target > 100:
-        if not _validate_confirmation_token(request.confirm_token, printer_id, "bed_temp"):
-            raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.set_bed_temperature(request.target)
-    return ControlResponse(
-        success=success,
-        message=f"Bed temperature set to {request.target}°C" if success else "Failed to set bed temperature"
-    )
-
-
-@router.post("/{printer_id}/control/temperature/nozzle", response_model=ControlResponse)
-async def set_nozzle_temperature(
-    printer_id: int,
-    request: NozzleTemperatureRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set the nozzle target temperature."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Warn for high temperatures
-    if request.target > 280 and not request.confirm_token:
-        token = _create_confirmation_token(printer_id, "nozzle_temp")
-        return ConfirmationRequired(
-            token=token,
-            warning=f"Setting nozzle to {request.target}°C is very high. Confirm?"
-        )
-
-    if request.target > 280:
-        if not _validate_confirmation_token(request.confirm_token, printer_id, "nozzle_temp"):
-            raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.set_nozzle_temperature(request.target, request.nozzle)
-    return ControlResponse(
-        success=success,
-        message=f"Nozzle {request.nozzle} temperature set to {request.target}°C" if success else "Failed to set nozzle temperature"
-    )
-
-
-@router.post("/{printer_id}/control/temperature/chamber", response_model=ControlResponse)
-async def set_chamber_temperature(
-    printer_id: int,
-    request: TemperatureRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set the chamber target temperature."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Warn for high temperatures (chamber typically maxes around 60°C)
-    if request.target > 60 and not request.confirm_token:
-        token = _create_confirmation_token(printer_id, "chamber_temp")
-        return ConfirmationRequired(
-            token=token,
-            warning=f"Setting chamber to {request.target}°C is very high. Confirm?"
-        )
-
-    if request.target > 60:
-        if not _validate_confirmation_token(request.confirm_token, printer_id, "chamber_temp"):
-            raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.set_chamber_temperature(request.target)
-    return ControlResponse(
-        success=success,
-        message=f"Chamber temperature set to {request.target}°C" if success else "Failed to set chamber temperature"
-    )
-
-
-# =============================================================================
-# Speed Control Endpoint
-# =============================================================================
-
-@router.post("/{printer_id}/control/speed", response_model=ControlResponse)
-async def set_print_speed(
-    printer_id: int,
-    request: SpeedRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set the print speed mode."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    speed_names = {1: "Silent", 2: "Standard", 3: "Sport", 4: "Ludicrous"}
-    success = client.set_print_speed(request.mode)
-    return ControlResponse(
-        success=success,
-        message=f"Speed set to {speed_names[request.mode]}" if success else "Failed to set speed"
-    )
-
-
-# =============================================================================
-# Extruder Control Endpoint
-# =============================================================================
-
-@router.post("/{printer_id}/control/extruder", response_model=ControlResponse)
-async def select_extruder(
-    printer_id: int,
-    request: ExtruderRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Select the active extruder for dual-nozzle printers."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    extruder_names = {0: "Right", 1: "Left"}
-    success = client.select_extruder(request.extruder)
-    return ControlResponse(
-        success=success,
-        message=f"Selected {extruder_names[request.extruder]} extruder" if success else "Failed to select extruder"
-    )
-
-
-# =============================================================================
-# Fan Control Endpoints
-# =============================================================================
-
-@router.post("/{printer_id}/control/fan/part", response_model=ControlResponse)
-async def set_part_fan(
-    printer_id: int,
-    request: FanRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set part cooling fan speed (0-100%)."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Convert percentage to 0-255
-    speed_255 = int(request.speed * 255 / 100)
-    success = client.set_part_fan(speed_255)
-    return ControlResponse(
-        success=success,
-        message=f"Part fan set to {request.speed}%" if success else "Failed to set part fan"
-    )
-
-
-@router.post("/{printer_id}/control/fan/aux", response_model=ControlResponse)
-async def set_aux_fan(
-    printer_id: int,
-    request: FanRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set auxiliary fan speed (0-100%)."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    speed_255 = int(request.speed * 255 / 100)
-    success = client.set_aux_fan(speed_255)
-    return ControlResponse(
-        success=success,
-        message=f"Aux fan set to {request.speed}%" if success else "Failed to set aux fan"
-    )
-
-
-@router.post("/{printer_id}/control/fan/chamber", response_model=ControlResponse)
-async def set_chamber_fan(
-    printer_id: int,
-    request: FanRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set chamber fan speed (0-100%)."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    speed_255 = int(request.speed * 255 / 100)
-    success = client.set_chamber_fan(speed_255)
-    return ControlResponse(
-        success=success,
-        message=f"Chamber fan set to {request.speed}%" if success else "Failed to set chamber fan"
-    )
-
-
-# =============================================================================
-# Air Conditioning Control Endpoint
-# =============================================================================
-
-class AirductModeRequest(BaseModel):
-    mode: str  # "cooling" or "heating"
-
-
-@router.post("/{printer_id}/control/airduct", response_model=ControlResponse)
-async def set_airduct_mode(
-    printer_id: int,
-    request: AirductModeRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set air conditioning mode (cooling or heating).
-
-    - Cooling: Suitable for PLA/PETG/TPU, filters and cools chamber air
-    - Heating: Suitable for ABS/ASA/PC/PA, circulates and heats chamber air, closes top exhaust flap
-    """
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    if request.mode not in ("cooling", "heating"):
-        raise HTTPException(status_code=400, detail="Mode must be 'cooling' or 'heating'")
-
-    success = client.set_airduct_mode(request.mode)
-    return ControlResponse(
-        success=success,
-        message=f"Air conditioning set to {request.mode}" if success else "Failed to set air conditioning mode"
-    )
-
-
-# =============================================================================
-# Light Control Endpoint
-# =============================================================================
-
-@router.post("/{printer_id}/control/light", response_model=ControlResponse)
-async def set_chamber_light(
-    printer_id: int,
-    request: LightRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Turn chamber light on or off."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    success = client.set_chamber_light(request.on)
-    return ControlResponse(
-        success=success,
-        message=f"Light turned {'on' if request.on else 'off'}" if success else "Failed to control light"
-    )
-
-
-# =============================================================================
-# Movement Control Endpoints
-# =============================================================================
-
-@router.post("/{printer_id}/control/home")
-async def home_axes(
-    printer_id: int,
-    request: HomeRequest = None,
-    db: AsyncSession = Depends(get_db),
-):
-    """Home the specified axes."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    axes = (request.axes if request else "XYZ").upper()
-
-    # Warn if homing during print
-    if client.state.state in ("RUNNING", "PAUSE"):
-        if not request or not request.confirm_token:
-            token = _create_confirmation_token(printer_id, "home")
-            return ConfirmationRequired(
-                token=token,
-                warning="Homing during an active print is not recommended. This may damage your print. Continue?"
-            )
-        if not _validate_confirmation_token(request.confirm_token, printer_id, "home"):
-            raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.home_axes(axes)
-    return ControlResponse(
-        success=success,
-        message=f"Homing {axes}" if success else "Failed to send home command"
-    )
-
-
-@router.post("/{printer_id}/control/move")
-async def move_axis(
-    printer_id: int,
-    request: MoveRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Move an axis by a relative distance."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Block movement during print unless confirmed
-    if client.state.state in ("RUNNING", "PAUSE"):
-        if not request.confirm_token:
-            token = _create_confirmation_token(printer_id, "move")
-            return ConfirmationRequired(
-                token=token,
-                warning="Manual movement during printing can damage your print. Are you sure?"
-            )
-        if not _validate_confirmation_token(request.confirm_token, printer_id, "move"):
-            raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.move_axis(request.axis.upper(), request.distance, request.speed)
-    direction = "+" if request.distance > 0 else ""
-    return ControlResponse(
-        success=success,
-        message=f"Moving {request.axis.upper()} {direction}{request.distance}mm" if success else "Failed to send move command"
-    )
-
-
-@router.post("/{printer_id}/control/motors/disable")
-async def disable_motors(
-    printer_id: int,
-    request: ConfirmableRequest = None,
-    db: AsyncSession = Depends(get_db),
-):
-    """Disable stepper motors. Warning: This will lose position."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Always require confirmation
-    if not request or not request.confirm_token:
-        token = _create_confirmation_token(printer_id, "disable_motors")
-        return ConfirmationRequired(
-            token=token,
-            warning="Disabling motors will cause the printer to lose its position. You must home before printing. Continue?"
-        )
-
-    if not _validate_confirmation_token(request.confirm_token, printer_id, "disable_motors"):
-        raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.disable_motors()
-    return ControlResponse(
-        success=success,
-        message="Motors disabled" if success else "Failed to disable motors"
-    )
-
-
-@router.post("/{printer_id}/control/motors/enable", response_model=ControlResponse)
-async def enable_motors(
-    printer_id: int,
-    db: AsyncSession = Depends(get_db),
-):
-    """Enable stepper motors."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    success = client.enable_motors()
-    return ControlResponse(
-        success=success,
-        message="Motors enabled" if success else "Failed to enable motors"
-    )
-
-
-# =============================================================================
-# AMS Control Endpoints
-# =============================================================================
-
-@router.post("/{printer_id}/control/ams/load", response_model=ControlResponse)
-async def ams_load_filament(
-    printer_id: int,
-    request: AMSLoadRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Load filament from a specific AMS tray."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Don't allow during print
-    if client.state.state == "RUNNING":
-        raise HTTPException(status_code=400, detail="Cannot change filament during print")
-
-    success = client.ams_load_filament(request.tray_id, request.extruder_id)
-    extruder_info = f" to extruder {request.extruder_id}" if request.extruder_id is not None else ""
-    return ControlResponse(
-        success=success,
-        message=f"Loading filament from tray {request.tray_id}{extruder_info}" if success else "Failed to load filament"
-    )
-
-
-@router.post("/{printer_id}/control/ams/unload", response_model=ControlResponse)
-async def ams_unload_filament(
-    printer_id: int,
-    db: AsyncSession = Depends(get_db),
-):
-    """Unload the currently loaded filament."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Don't allow during print
-    if client.state.state == "RUNNING":
-        raise HTTPException(status_code=400, detail="Cannot unload filament during print")
-
-    success = client.ams_unload_filament()
-    return ControlResponse(
-        success=success,
-        message="Unloading filament" if success else "Failed to unload filament"
-    )
-
-
-@router.post("/{printer_id}/control/ams/refresh-tray", response_model=ControlResponse)
-async def ams_refresh_tray(
-    printer_id: int,
-    request: AMSRefreshTrayRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Trigger RFID re-read for a specific AMS tray."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    success, message = client.ams_refresh_tray(request.ams_id, request.tray_id)
-    return ControlResponse(success=success, message=message)
-
-
-@router.post("/{printer_id}/control/ams/filament-setting", response_model=ControlResponse)
-async def ams_set_filament_setting(
-    printer_id: int,
-    request: AMSFilamentSettingRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Set filament settings for an AMS tray including K (pressure advance) value."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    success = client.ams_set_filament_setting(
-        ams_id=request.ams_id,
-        tray_id=request.tray_id,
-        tray_info_idx=request.tray_info_idx,
-        tray_type=request.tray_type,
-        tray_sub_brands=request.tray_sub_brands,
-        tray_color=request.tray_color,
-        nozzle_temp_min=request.nozzle_temp_min,
-        nozzle_temp_max=request.nozzle_temp_max,
-        k=request.k,
-    )
-    return ControlResponse(
-        success=success,
-        message=f"Updated AMS {request.ams_id} tray {request.tray_id} with K={request.k}" if success else "Failed to update filament setting"
-    )
-
-
-# =============================================================================
-# Advanced: G-code Command
-# =============================================================================
-
-@router.post("/{printer_id}/control/gcode")
-async def send_gcode(
-    printer_id: int,
-    request: GcodeRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Send raw G-code command(s). Advanced users only."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    # Require confirmation for any G-code
-    if not request.confirm_token:
-        token = _create_confirmation_token(printer_id, "gcode")
-        return ConfirmationRequired(
-            token=token,
-            warning="Sending raw G-code can damage your printer if used incorrectly. Are you sure?"
-        )
-
-    if not _validate_confirmation_token(request.confirm_token, printer_id, "gcode"):
-        raise HTTPException(status_code=400, detail="Invalid or expired confirmation token")
-
-    success = client.send_gcode(request.command)
-    return ControlResponse(
-        success=success,
-        message="G-code sent" if success else "Failed to send G-code"
-    )
-
-
-# =============================================================================
-# Camera Settings Endpoints
-# =============================================================================
-
-@router.post("/{printer_id}/control/camera/timelapse", response_model=ControlResponse)
-async def set_timelapse(
-    printer_id: int,
-    request: CameraSettingRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Enable or disable timelapse recording."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    success = client.set_timelapse(request.enable)
-    return ControlResponse(
-        success=success,
-        message=f"Timelapse {'enabled' if request.enable else 'disabled'}" if success else "Failed to set timelapse"
-    )
-
-
-@router.post("/{printer_id}/control/camera/liveview", response_model=ControlResponse)
-async def set_liveview(
-    printer_id: int,
-    request: CameraSettingRequest,
-    db: AsyncSession = Depends(get_db),
-):
-    """Enable or disable live view / camera streaming."""
-    await get_printer_or_404(printer_id, db)
-    client = get_mqtt_client_or_503(printer_id)
-
-    success = client.set_liveview(request.enable)
-    return ControlResponse(
-        success=success,
-        message=f"Live view {'enabled' if request.enable else 'disabled'}" if success else "Failed to set live view"
-    )
-
-
-# =============================================================================
-# Status Refresh Endpoint
-# =============================================================================
-
-@router.post("/{printer_id}/control/refresh", response_model=ControlResponse)
-async def refresh_status(
-    printer_id: int,
-    db: AsyncSession = Depends(get_db),
-):
-    """Request a full status update from the printer.
-
-    This sends a 'pushall' command to get the latest data including nozzle info,
-    AMS status, and all other printer state.
-    """
-    await get_printer_or_404(printer_id, db)
-
-    success = printer_manager.request_status_update(printer_id)
-    if not success:
-        raise HTTPException(status_code=503, detail="Printer not connected")
-
-    return ControlResponse(
-        success=success,
-        message="Status refresh requested"
-    )

+ 1 - 2
backend/app/main.py

@@ -54,7 +54,7 @@ from fastapi.responses import FileResponse
 from backend.app.core.database import init_db, async_session
 from sqlalchemy import select, or_
 from backend.app.core.websocket import ws_manager
-from backend.app.api.routes import printers, archives, websocket, filaments, cloud, smart_plugs, print_queue, kprofiles, notifications, spoolman, updates, maintenance, printer_control, camera
+from backend.app.api.routes import printers, archives, websocket, filaments, cloud, smart_plugs, print_queue, kprofiles, notifications, spoolman, updates, maintenance, camera
 from backend.app.api.routes import settings as settings_routes
 from backend.app.services.notification_service import notification_service
 from backend.app.services.printer_manager import (
@@ -1021,7 +1021,6 @@ app.include_router(notifications.router, prefix=app_settings.api_prefix)
 app.include_router(spoolman.router, prefix=app_settings.api_prefix)
 app.include_router(updates.router, prefix=app_settings.api_prefix)
 app.include_router(maintenance.router, prefix=app_settings.api_prefix)
-app.include_router(printer_control.router, prefix=app_settings.api_prefix)
 app.include_router(camera.router, prefix=app_settings.api_prefix)
 app.include_router(websocket.router, prefix=app_settings.api_prefix)
 

+ 11 - 28
frontend/package-lock.json

@@ -97,7 +97,6 @@
       "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@babel/code-frame": "^7.27.1",
         "@babel/generator": "^7.28.5",
@@ -381,7 +380,6 @@
       "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
       "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@dnd-kit/accessibility": "^3.1.1",
         "@dnd-kit/utilities": "^3.2.2",
@@ -1027,6 +1025,16 @@
         "@floating-ui/utils": "^0.2.10"
       }
     },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+      "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+      "optional": true,
+      "dependencies": {
+        "@floating-ui/core": "^1.7.3",
+        "@floating-ui/utils": "^0.2.10"
+      }
+    },
     "node_modules/@floating-ui/utils": {
       "version": "0.2.10",
       "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
@@ -1807,7 +1815,6 @@
       "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.11.1.tgz",
       "integrity": "sha512-q7uzYrCq40JOIi6lceWe2HuA8tSr97iPwP/xtJd0bZjyL1rWhUyqxMb7y+aq4RcELrx/aNRa2JIvLtRRdy02Dg==",
       "license": "MIT",
-      "peer": true,
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/ueberdosis"
@@ -2056,7 +2063,6 @@
       "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.11.1.tgz",
       "integrity": "sha512-XJRN9pOPMi3SsaKv4qM8WBEi3YDrjXYtYlAlZutQe1JpdKykSjLwwYq7k3V8UHqR3YKxyOV8HTYOYoOaZ9TMTQ==",
       "license": "MIT",
-      "peer": true,
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/ueberdosis"
@@ -2162,7 +2168,6 @@
       "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-3.11.1.tgz",
       "integrity": "sha512-KLLrABvf609/Z4dPChRowvpqeefYiq5csEj4Ogfp4EFd3KqDvPZIoFepau1+BW4gOAlm8UK+ig+fOLgnUzH7ww==",
       "license": "MIT",
-      "peer": true,
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/ueberdosis"
@@ -2189,7 +2194,6 @@
       "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.11.1.tgz",
       "integrity": "sha512-/xXJdV+EVvSQv2slvAUChb5iGVv5K0EqBqxPGAAuBHdIc4Y7Id1aaKKSiyDmqon+kjSnnQIIda9oUt+o/Z66uA==",
       "license": "MIT",
-      "peer": true,
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/ueberdosis"
@@ -2204,7 +2208,6 @@
       "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.11.1.tgz",
       "integrity": "sha512-8RIUhlEoCFGsbdNb+EUdQctG1Wnd7rl4wlMLS6giO7UcZT5dVfg625eMZVrl0/kA7JBJdKLIuqNmzzQ0MxsJEw==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "prosemirror-changeset": "^2.3.0",
         "prosemirror-collab": "^1.3.1",
@@ -2449,7 +2452,6 @@
       "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "undici-types": "~7.16.0"
       }
@@ -2459,7 +2461,6 @@
       "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
       "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "csstype": "^3.2.2"
       }
@@ -2469,7 +2470,6 @@
       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
       "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
       "license": "MIT",
-      "peer": true,
       "peerDependencies": {
         "@types/react": "^19.2.0"
       }
@@ -2553,7 +2553,6 @@
       "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@typescript-eslint/scope-manager": "8.48.0",
         "@typescript-eslint/types": "8.48.0",
@@ -2811,7 +2810,6 @@
       "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "bin": {
         "acorn": "bin/acorn"
       },
@@ -2954,7 +2952,6 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "baseline-browser-mapping": "^2.8.25",
         "caniuse-lite": "^1.0.30001754",
@@ -3390,7 +3387,6 @@
       "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
         "@eslint-community/regexpp": "^4.12.1",
@@ -3820,7 +3816,6 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.28.4"
       },
@@ -4631,7 +4626,6 @@
       "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "engines": {
         "node": ">=12"
       },
@@ -4659,7 +4653,6 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "nanoid": "^3.3.11",
         "picocolors": "^1.1.1",
@@ -4804,7 +4797,6 @@
       "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
       "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "orderedmap": "^2.0.0"
       }
@@ -4834,7 +4826,6 @@
       "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
       "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "prosemirror-model": "^1.0.0",
         "prosemirror-transform": "^1.0.0",
@@ -4883,7 +4874,6 @@
       "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz",
       "integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "prosemirror-model": "^1.20.0",
         "prosemirror-state": "^1.0.0",
@@ -4914,7 +4904,6 @@
       "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
       "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
       "license": "MIT",
-      "peer": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -4924,7 +4913,6 @@
       "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
       "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "scheduler": "^0.27.0"
       },
@@ -4971,7 +4959,6 @@
       "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
       "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@types/use-sync-external-store": "^0.0.6",
         "use-sync-external-store": "^1.4.0"
@@ -5087,8 +5074,7 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
       "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/redux-thunk": {
       "version": "3.1.0",
@@ -5359,7 +5345,6 @@
       "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
       "devOptional": true,
       "license": "Apache-2.0",
-      "peer": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
@@ -5489,7 +5474,6 @@
       "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "esbuild": "^0.25.0",
         "fdir": "^6.5.0",
@@ -5642,7 +5626,6 @@
       "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "funding": {
         "url": "https://github.com/sponsors/colinhacks"
       }

+ 0 - 2
frontend/src/App.tsx

@@ -8,7 +8,6 @@ import { StatsPage } from './pages/StatsPage';
 import { SettingsPage } from './pages/SettingsPage';
 import { ProfilesPage } from './pages/ProfilesPage';
 import { MaintenancePage } from './pages/MaintenancePage';
-import { ControlPage } from './pages/ControlPage';
 import { useWebSocket } from './hooks/useWebSocket';
 import { ThemeProvider } from './contexts/ThemeContext';
 import { ToastProvider } from './contexts/ToastContext';
@@ -42,7 +41,6 @@ function App() {
                   <Route path="stats" element={<StatsPage />} />
                   <Route path="profiles" element={<ProfilesPage />} />
                   <Route path="maintenance" element={<MaintenancePage />} />
-                  <Route path="control" element={<ControlPage />} />
                   <Route path="settings" element={<SettingsPage />} />
                 </Route>
               </Routes>

+ 1 - 200
frontend/src/api/client.ts

@@ -638,25 +638,6 @@ export interface UpdateStatus {
   error: string | null;
 }
 
-// Printer Control types
-export interface ControlResponse {
-  success: boolean;
-  message: string;
-}
-
-export interface ConfirmationRequired {
-  requires_confirmation: boolean;
-  token: string;
-  warning: string;
-  expires_in: number;
-}
-
-export type ControlResult = ControlResponse | ConfirmationRequired;
-
-export function isConfirmationRequired(result: ControlResult): result is ConfirmationRequired {
-  return 'requires_confirmation' in result && result.requires_confirmation === true;
-}
-
 // Maintenance types
 export interface MaintenanceType {
   id: number;
@@ -1199,191 +1180,11 @@ export const api = {
       { method: 'PATCH' }
     ),
 
-  // Printer Control
-  pausePrint: (printerId: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/pause`, { method: 'POST' }),
-  resumePrint: (printerId: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/resume`, { method: 'POST' }),
-  stopPrint: (printerId: number, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/stop`, {
-      method: 'POST',
-      body: JSON.stringify({ confirm_token: confirmToken }),
-    }),
-  setBedTemperature: (printerId: number, target: number, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/temperature/bed`, {
-      method: 'POST',
-      body: JSON.stringify({ target, confirm_token: confirmToken }),
-    }),
-  setNozzleTemperature: (printerId: number, target: number, nozzle = 0, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/temperature/nozzle`, {
-      method: 'POST',
-      body: JSON.stringify({ target, nozzle, confirm_token: confirmToken }),
-    }),
-  setChamberTemperature: (printerId: number, target: number, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/temperature/chamber`, {
-      method: 'POST',
-      body: JSON.stringify({ target, confirm_token: confirmToken }),
-    }),
-  setPrintSpeed: (printerId: number, mode: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/speed`, {
-      method: 'POST',
-      body: JSON.stringify({ mode }),
-    }),
-  setPartFan: (printerId: number, speed: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/fan/part`, {
-      method: 'POST',
-      body: JSON.stringify({ speed }),
-    }),
-  setAuxFan: (printerId: number, speed: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/fan/aux`, {
-      method: 'POST',
-      body: JSON.stringify({ speed }),
-    }),
-  setChamberFan: (printerId: number, speed: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/fan/chamber`, {
-      method: 'POST',
-      body: JSON.stringify({ speed }),
-    }),
-  setAirductMode: (printerId: number, mode: 'cooling' | 'heating') =>
-    request<ControlResponse>(`/printers/${printerId}/control/airduct`, {
-      method: 'POST',
-      body: JSON.stringify({ mode }),
-    }),
-  setChamberLight: (printerId: number, on: boolean) =>
-    request<ControlResponse>(`/printers/${printerId}/control/light`, {
-      method: 'POST',
-      body: JSON.stringify({ on }),
-    }),
-  selectExtruder: (printerId: number, extruder: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/extruder`, {
-      method: 'POST',
-      body: JSON.stringify({ extruder }),
-    }),
-  homeAxes: (printerId: number, axes = 'XYZ', confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/home`, {
-      method: 'POST',
-      body: JSON.stringify({ axes, confirm_token: confirmToken }),
-    }),
-  moveAxis: (printerId: number, axis: string, distance: number, speed = 3000, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/move`, {
-      method: 'POST',
-      body: JSON.stringify({ axis, distance, speed, confirm_token: confirmToken }),
-    }),
-  disableMotors: (printerId: number, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/motors/disable`, {
-      method: 'POST',
-      body: JSON.stringify({ confirm_token: confirmToken }),
-    }),
-  enableMotors: (printerId: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/motors/enable`, { method: 'POST' }),
-  amsLoadFilament: (printerId: number, trayId: number, extruderId?: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/ams/load`, {
-      method: 'POST',
-      body: JSON.stringify({ tray_id: trayId, extruder_id: extruderId }),
-    }),
-  amsUnloadFilament: (printerId: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/ams/unload`, { method: 'POST' }),
-  amsSetFilamentSetting: (printerId: number, data: {
-    ams_id: number;
-    tray_id: number;
-    tray_info_idx: string;
-    tray_type: string;
-    tray_sub_brands: string;
-    tray_color: string;
-    nozzle_temp_min: number;
-    nozzle_temp_max: number;
-    k: number;
-  }) =>
-    request<ControlResponse>(`/printers/${printerId}/control/ams/filament-setting`, {
-      method: 'POST',
-      body: JSON.stringify(data),
-    }),
-  sendGcode: (printerId: number, command: string, confirmToken?: string) =>
-    request<ControlResult>(`/printers/${printerId}/control/gcode`, {
-      method: 'POST',
-      body: JSON.stringify({ command, confirm_token: confirmToken }),
-    }),
+  // Camera
   getCameraStreamUrl: (printerId: number, fps = 10) =>
     `${API_BASE}/printers/${printerId}/camera/stream?fps=${fps}`,
   getCameraSnapshotUrl: (printerId: number) =>
     `${API_BASE}/printers/${printerId}/camera/snapshot`,
   testCameraConnection: (printerId: number) =>
     request<{ success: boolean; message?: string; error?: string }>(`/printers/${printerId}/camera/test`),
-  setTimelapse: (printerId: number, enable: boolean) =>
-    request<ControlResponse>(`/printers/${printerId}/control/camera/timelapse`, {
-      method: 'POST',
-      body: JSON.stringify({ enable }),
-    }),
-  setLiveview: (printerId: number, enable: boolean) =>
-    request<ControlResponse>(`/printers/${printerId}/control/camera/liveview`, {
-      method: 'POST',
-      body: JSON.stringify({ enable }),
-    }),
-
-  // Request full status update from printer (pushall)
-  refreshStatus: (printerId: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/refresh`, {
-      method: 'POST',
-    }),
-
-  // Refresh a specific AMS tray (trigger RFID re-read)
-  refreshAmsTray: (printerId: number, amsId: number, trayId: number) =>
-    request<ControlResponse>(`/printers/${printerId}/control/ams/refresh-tray`, {
-      method: 'POST',
-      body: JSON.stringify({ ams_id: amsId, tray_id: trayId }),
-    }),
-
-  // Print Options (AI Detection)
-  setPrintOption: (
-    printerId: number,
-    moduleName: string,
-    enabled: boolean,
-    printHalt = true,
-    sensitivity = 'medium'
-  ) => {
-    const params = new URLSearchParams({
-      module_name: moduleName,
-      enabled: String(enabled),
-      print_halt: String(printHalt),
-      sensitivity,
-    });
-    return request<{
-      success: boolean;
-      module_name: string;
-      enabled: boolean;
-      print_halt: boolean;
-      sensitivity: string;
-    }>(`/printers/${printerId}/print-options?${params}`, {
-      method: 'POST',
-    });
-  },
-
-  // Calibration
-  startCalibration: (
-    printerId: number,
-    options: {
-      bed_leveling?: boolean;
-      vibration?: boolean;
-      motor_noise?: boolean;
-      nozzle_offset?: boolean;
-      high_temp_heatbed?: boolean;
-    }
-  ) => {
-    const params = new URLSearchParams();
-    if (options.bed_leveling) params.append('bed_leveling', 'true');
-    if (options.vibration) params.append('vibration', 'true');
-    if (options.motor_noise) params.append('motor_noise', 'true');
-    if (options.nozzle_offset) params.append('nozzle_offset', 'true');
-    if (options.high_temp_heatbed) params.append('high_temp_heatbed', 'true');
-    return request<{
-      success: boolean;
-      bed_leveling: boolean;
-      vibration: boolean;
-      motor_noise: boolean;
-      nozzle_offset: boolean;
-      high_temp_heatbed: boolean;
-    }>(`/printers/${printerId}/calibration?${params}`, {
-      method: 'POST',
-    });
-  },
 };

+ 1 - 2
frontend/src/components/Layout.tsx

@@ -1,6 +1,6 @@
 import { useState, useEffect, useCallback, useRef } from 'react';
 import { NavLink, Outlet, useNavigate, useLocation } from 'react-router-dom';
-import { Printer, Archive, Calendar, BarChart3, Cloud, Settings, Sun, Moon, ChevronLeft, ChevronRight, Keyboard, Github, GripVertical, ArrowUpCircle, Wrench, Gamepad2, type LucideIcon } from 'lucide-react';
+import { Printer, Archive, Calendar, BarChart3, Cloud, Settings, Sun, Moon, ChevronLeft, ChevronRight, Keyboard, Github, GripVertical, ArrowUpCircle, Wrench, type LucideIcon } from 'lucide-react';
 import { useTranslation } from 'react-i18next';
 import { useTheme } from '../contexts/ThemeContext';
 import { KeyboardShortcutsModal } from './KeyboardShortcutsModal';
@@ -16,7 +16,6 @@ interface NavItem {
 
 export const defaultNavItems: NavItem[] = [
   { id: 'printers', to: '/', icon: Printer, labelKey: 'nav.printers' },
-  { id: 'control', to: '/control', icon: Gamepad2, labelKey: 'nav.control' },
   { id: 'archives', to: '/archives', icon: Archive, labelKey: 'nav.archives' },
   { id: 'queue', to: '/queue', icon: Calendar, labelKey: 'nav.queue' },
   { id: 'stats', to: '/stats', icon: BarChart3, labelKey: 'nav.stats' },

+ 0 - 100
frontend/src/components/control/AMSHumidityModal.tsx

@@ -1,100 +0,0 @@
-import { useEffect } from 'react';
-import { X } from 'lucide-react';
-
-interface AMSHumidityModalProps {
-  humidity: number;
-  temperature: number;
-  dryingStatus: 'idle' | 'drying';
-  remainingTime?: number; // in minutes
-  onClose: () => void;
-}
-
-// Single humidity icon that fills based on level (larger version for modal)
-function HumidityIconLarge({ humidity }: { humidity: number }) {
-  const getIconSrc = (): string => {
-    if (humidity < 25) return '/icons/humidity-empty.svg';
-    if (humidity < 40) return '/icons/humidity-half.svg';
-    return '/icons/humidity-full.svg';
-  };
-
-  return (
-    <img
-      src={getIconSrc()}
-      alt=""
-      className="w-16 h-24"
-    />
-  );
-}
-
-export function AMSHumidityModal({
-  humidity,
-  temperature,
-  dryingStatus,
-  remainingTime,
-  onClose,
-}: AMSHumidityModalProps) {
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') onClose();
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose]);
-
-  const formatRemainingTime = () => {
-    if (dryingStatus === 'idle') return 'Idle';
-    if (!remainingTime) return 'Drying...';
-    const hours = Math.floor(remainingTime / 60);
-    const mins = remainingTime % 60;
-    if (hours > 0) {
-      return `${hours}h ${mins}m`;
-    }
-    return `${mins}m`;
-  };
-
-  return (
-    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
-      <div className="bg-white dark:bg-bambu-dark-secondary rounded-lg shadow-xl max-w-md w-full">
-        {/* Header */}
-        <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-bambu-dark-tertiary">
-          <h2 className="text-lg font-semibold text-gray-900 dark:text-white">Current AMS humidity</h2>
-          <button
-            onClick={onClose}
-            className="p-1 hover:bg-gray-100 dark:hover:bg-bambu-dark-tertiary rounded-lg transition-colors"
-          >
-            <X className="w-5 h-5 text-gray-500 dark:text-bambu-gray" />
-          </button>
-        </div>
-
-        {/* Content */}
-        <div className="p-6">
-          {/* Large humidity icon */}
-          <div className="flex flex-col items-center mb-6">
-            <HumidityIconLarge humidity={humidity} />
-            <div className="flex items-center gap-2 mt-2 text-gray-600 dark:text-bambu-gray">
-              <span className="text-yellow-500">❊</span>
-              <span>{dryingStatus === 'idle' ? 'Idle' : 'Drying'}</span>
-            </div>
-          </div>
-
-          {/* Stats row */}
-          <div className="grid grid-cols-3 gap-4 text-center">
-            <div>
-              <div className="text-sm text-gray-500 dark:text-bambu-gray mb-1">Humidity</div>
-              <div className="text-lg font-medium text-gray-900 dark:text-white">{humidity}%</div>
-            </div>
-            <div>
-              <div className="text-sm text-gray-500 dark:text-bambu-gray mb-1">Temperature</div>
-              <div className="text-lg font-medium text-gray-900 dark:text-white">{temperature} °C</div>
-            </div>
-            <div>
-              <div className="text-sm text-gray-500 dark:text-bambu-gray mb-1">Remaining Time</div>
-              <div className="text-lg font-medium text-gray-900 dark:text-white">{formatRemainingTime()}</div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}

+ 0 - 674
frontend/src/components/control/AMSMaterialsModal.tsx

@@ -1,674 +0,0 @@
-import { useState, useEffect } from 'react';
-import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
-import { X, ExternalLink } from 'lucide-react';
-import { api } from '../../api/client';
-import type { AMSTray, KProfile } from '../../api/client';
-
-interface AMSMaterialsModalProps {
-  tray: AMSTray;
-  amsId: number;  // AMS unit ID (0, 1, 2, 3)
-  slotLabel: string;
-  printerId: number;
-  printerModel: string;  // e.g., "H2D", "X1C", "P1S"
-  nozzleDiameter?: string;
-  extruderId?: number;  // 0=right nozzle, 1=left nozzle (for filtering K-profiles)
-  onClose: () => void;
-  onConfirm?: (data: MaterialSettings) => void;
-}
-
-// Extract base filament name (without printer/nozzle suffix)
-// e.g., "# Bambu PLA Basic @BBL H2D" -> "# Bambu PLA Basic"
-// e.g., "Devil Design PLA Basic @Bambu Lab H2D 0.4 nozzle" -> "Devil Design PLA Basic"
-function getBaseFilamentName(name: string): string {
-  const atIndex = name.indexOf(' @');
-  return atIndex > 0 ? name.substring(0, atIndex) : name;
-}
-
-// Get normalized name for deduplication (without # prefix and @ suffix)
-// e.g., "# Bambu PLA Basic @BBL H2D" -> "Bambu PLA Basic"
-// e.g., "Bambu PLA Basic @BBL X1C" -> "Bambu PLA Basic"
-function getNormalizedName(name: string): string {
-  let normalized = name;
-  if (normalized.startsWith('# ')) {
-    normalized = normalized.substring(2);
-  }
-  const atIndex = normalized.indexOf(' @');
-  return atIndex > 0 ? normalized.substring(0, atIndex) : normalized;
-}
-
-// Get clean name for K-profile matching (without # prefix and @ suffix)
-function getCleanFilamentName(name: string): string {
-  let cleanName = name;
-  if (cleanName.startsWith('# ')) {
-    cleanName = cleanName.substring(2);
-  }
-  const atIndex = cleanName.indexOf(' @');
-  return atIndex > 0 ? cleanName.substring(0, atIndex) : cleanName;
-}
-
-export interface MaterialSettings {
-  filamentType: string;
-  color: string;
-  nozzleTempMax: number;
-  nozzleTempMin: number;
-  kProfile: string | null;
-  kValue: number;
-}
-
-function hexToRgb(hex: string | null): string {
-  if (!hex) return 'rgb(128, 128, 128)';
-  const cleanHex = hex.replace('#', '').substring(0, 6);
-  const rParsed = parseInt(cleanHex.substring(0, 2), 16);
-  const gParsed = parseInt(cleanHex.substring(2, 4), 16);
-  const bParsed = parseInt(cleanHex.substring(4, 6), 16);
-  const r = isNaN(rParsed) ? 128 : rParsed;
-  const g = isNaN(gParsed) ? 128 : gParsed;
-  const b = isNaN(bParsed) ? 128 : bParsed;
-  return `rgb(${r}, ${g}, ${b})`;
-}
-
-function trayColorToHex(trayColor: string | null): string {
-  if (!trayColor) return '#808080';
-  // Tray color comes as RRGGBBAA, we need #RRGGBB
-  const cleanHex = trayColor.replace('#', '').substring(0, 6);
-  return `#${cleanHex}`;
-}
-
-function hexToTrayColor(hex: string): string {
-  // Convert #RRGGBB to RRGGBBFF (with full opacity)
-  const cleanHex = hex.replace('#', '').toUpperCase();
-  return cleanHex.length === 6 ? `${cleanHex}FF` : `${cleanHex.substring(0, 6)}FF`;
-}
-
-// Check if tray has valid Bambu Lab UUID (32-char hex)
-function isBambuLabSpool(trayUuid: string | null): boolean {
-  if (!trayUuid) return false;
-  const uuid = trayUuid.trim();
-  if (uuid.length !== 32) return false;
-  if (uuid === '00000000000000000000000000000000') return false;
-  return /^[0-9a-fA-F]{32}$/.test(uuid);
-}
-
-// Bambu Lab color codes from tray_id_name (e.g., "A00-Y2" -> "Sunflower Yellow")
-const BAMBU_COLOR_CODES: Record<string, string> = {
-  'Y2': 'Sunflower Yellow', 'Y0': 'Yellow', 'Y1': 'Lemon Yellow',
-  'K0': 'Black', 'W0': 'White', 'W1': 'Ivory White',
-  'R0': 'Red', 'R1': 'Scarlet Red', 'R2': 'Magenta',
-  'B0': 'Blue', 'B1': 'Navy Blue', 'B2': 'Sky Blue', 'B3': 'Cyan',
-  'G0': 'Green', 'G1': 'Grass Green', 'G2': 'Jade Green',
-  'O0': 'Orange', 'O1': 'Mandarin Orange',
-  'P0': 'Purple', 'P1': 'Pink', 'P2': 'Sakura Pink',
-  'N0': 'Gray', 'N1': 'Silver Gray', 'N2': 'Charcoal',
-  'D0': 'Brown', 'D1': 'Chocolate',
-  'T0': 'Titan Gray', 'T1': 'Jade White',
-};
-
-function getColorNameFromTrayId(trayIdName: string | null): string | null {
-  if (!trayIdName) return null;
-  // tray_id_name format: "A00-Y2" or "G02-K0" - color code is after the dash
-  const parts = trayIdName.split('-');
-  if (parts.length < 2) return null;
-  const colorCode = parts[1];
-  return BAMBU_COLOR_CODES[colorCode] || null;
-}
-
-// Find best matching K-profile for a filament
-// Profiles have names like "HF Bambu PLA Basic Sunflower Yellow", "High Flow_Bambu PLA Basic"
-// tray_sub_brands is like "PLA Basic", "PETG HF"
-// tray_type is like "PLA", "PETG"
-// colorName is like "Sunflower Yellow"
-function findBestKProfile(
-  profiles: KProfile[],
-  traySubBrands: string | null,
-  trayType: string | null,
-  colorName: string | null
-): KProfile | null {
-  if (!profiles.length) return null;
-
-  const subBrands = traySubBrands?.toLowerCase() || '';
-  const type = trayType?.toUpperCase() || '';
-  const color = colorName?.toLowerCase() || '';
-
-  // Priority 1: Match tray_sub_brands AND color (e.g., "PLA Basic" + "Sunflower Yellow")
-  if (subBrands && color) {
-    const exactColorMatch = profiles.find(p => {
-      const name = p.name.toLowerCase();
-      return name.includes(subBrands) && name.includes(color);
-    });
-    if (exactColorMatch) return exactColorMatch;
-  }
-
-  // Priority 2: Match tray_sub_brands without color (e.g., "PLA Basic" in "High Flow_Bambu PLA Basic")
-  if (subBrands) {
-    const subBrandsMatch = profiles.find(p =>
-      p.name.toLowerCase().includes(subBrands)
-    );
-    if (subBrandsMatch) return subBrandsMatch;
-  }
-
-  // Priority 3: Match filament type in profile name (e.g., "PLA" in "High Flow_Bambu PLA Basic")
-  if (type) {
-    const typeMatches = profiles.filter(p =>
-      p.name.toUpperCase().includes(type)
-    );
-
-    if (typeMatches.length > 0) {
-      // Prefer "Basic" profiles for generic type matching
-      const basicMatch = typeMatches.find(p =>
-        p.name.toLowerCase().includes('basic')
-      );
-      if (basicMatch) return basicMatch;
-
-      return typeMatches[0];
-    }
-  }
-
-  // Priority 4: Default profile
-  const defaultProfile = profiles.find(p =>
-    p.name.toLowerCase() === 'default' || p.slot_id === 0
-  );
-  return defaultProfile || null;
-}
-
-export function AMSMaterialsModal({
-  tray,
-  amsId,
-  slotLabel,
-  printerId,
-  printerModel,
-  nozzleDiameter = '0.4',
-  extruderId = 0,
-  onClose,
-  onConfirm,
-}: AMSMaterialsModalProps) {
-  const queryClient = useQueryClient();
-
-  // Determine slot type
-  const isEmpty = !tray.tray_type || tray.tray_type === '' || tray.tray_type === 'NONE';
-  const isBambuSpool = isBambuLabSpool(tray.tray_uuid);
-  const isEditable = isEmpty || !isBambuSpool;
-
-  // Fetch K-profiles
-  const { data: kProfilesData } = useQuery({
-    queryKey: ['kprofiles', printerId, nozzleDiameter],
-    queryFn: () => api.getKProfiles(printerId, nozzleDiameter),
-  });
-
-  // Fetch cloud filament presets
-  const { data: cloudSettings } = useQuery({
-    queryKey: ['cloudSettings'],
-    queryFn: () => api.getCloudSettings(),
-  });
-
-  // Fetch saved slot preset mapping
-  const { data: savedPreset } = useQuery({
-    queryKey: ['slotPreset', printerId, amsId, tray.id],
-    queryFn: () => api.getSlotPreset(printerId, amsId, tray.id),
-    enabled: isEditable,  // Only fetch for editable (non-Bambu) slots
-  });
-
-  // Mutation for saving preset mapping
-  const savePresetMutation = useMutation({
-    mutationFn: ({ presetId, presetName }: { presetId: string; presetName: string }) =>
-      api.saveSlotPreset(printerId, amsId, tray.id, presetId, presetName),
-    onSuccess: () => {
-      queryClient.invalidateQueries({ queryKey: ['slotPreset', printerId, amsId, tray.id] });
-    },
-  });
-
-  // Mutation for saving filament settings to printer (including K value)
-  const saveFilamentSettingMutation = useMutation({
-    mutationFn: (data: {
-      ams_id: number;
-      tray_id: number;
-      tray_info_idx: string;
-      tray_type: string;
-      tray_sub_brands: string;
-      tray_color: string;
-      nozzle_temp_min: number;
-      nozzle_temp_max: number;
-      k: number;
-    }) => api.amsSetFilamentSetting(printerId, data),
-    onSuccess: (result) => {
-      console.log('[AMSMaterialsModal] saveFilamentSettingMutation SUCCESS:', result);
-    },
-    onError: (error) => {
-      console.error('[AMSMaterialsModal] saveFilamentSettingMutation ERROR:', error);
-    },
-  });
-
-  // Get filament presets from cloud settings:
-  // 1. Filter out presets starting with "#" (experimental/special presets)
-  // 2. Prioritize current printer model, then deduplicate by base name
-  // 3. Sort alphabetically
-  const filamentPresets = (() => {
-    const allPresets = cloudSettings?.filament || [];
-
-    // Filter out presets starting with "#" (experimental/special presets)
-    const filteredPresets = allPresets.filter(p => !p.name.startsWith('# '));
-
-    // Sort presets: current printer model first, then others
-    const modelPattern = new RegExp(`@.*\\b${printerModel}\\b`, 'i');
-    const sortedByModel = [...filteredPresets].sort((a, b) => {
-      const aMatches = modelPattern.test(a.name);
-      const bMatches = modelPattern.test(b.name);
-      if (aMatches && !bMatches) return -1;
-      if (!aMatches && bMatches) return 1;
-      return 0;
-    });
-
-    // Remove duplicates - keep first occurrence of each normalized name
-    // (presets for current printer model will be kept due to sorting above)
-    // Uses normalized name (without # prefix) so "# Bambu ABS" and "Bambu ABS" are treated as same
-    const seenNormalizedNames = new Set<string>();
-    const unique = sortedByModel.filter(p => {
-      const normalizedName = getNormalizedName(p.name);
-      if (seenNormalizedNames.has(normalizedName)) return false;
-      seenNormalizedNames.add(normalizedName);
-      return true;
-    });
-
-    // Sort by base name alphabetically
-    const sorted = unique.sort((a, b) =>
-      getBaseFilamentName(a.name).localeCompare(getBaseFilamentName(b.name))
-    );
-
-    return sorted;
-  })();
-
-  // State
-  const [selectedPresetId, setSelectedPresetId] = useState<string | null>(null);
-  const [selectedPresetName, setSelectedPresetName] = useState<string | null>(null);
-  const [selectedFilamentType, setSelectedFilamentType] = useState(tray.tray_type || '');
-  const [selectedColor, setSelectedColor] = useState(trayColorToHex(tray.tray_color));
-  const [nozzleTempMax, setNozzleTempMax] = useState(tray.nozzle_temp_max || 220);
-  const [nozzleTempMin, setNozzleTempMin] = useState(tray.nozzle_temp_min || 190);
-  const [selectedKProfile, setSelectedKProfile] = useState<string>('');
-  const [kValue, setKValue] = useState(tray.k || 0);
-
-  // Load saved preset when data is available
-  useEffect(() => {
-    if (savedPreset && !selectedPresetId) {
-      setSelectedPresetId(savedPreset.preset_id);
-      setSelectedPresetName(savedPreset.preset_name);
-    }
-  }, [savedPreset, selectedPresetId]);
-
-  // Handle filament preset selection from dropdown
-  const handlePresetSelect = (settingId: string) => {
-    if (!settingId) {
-      setSelectedPresetId(null);
-      setSelectedPresetName(null);
-      return;
-    }
-    setSelectedPresetId(settingId);
-    const preset = filamentPresets.find(f => f.setting_id === settingId);
-    if (preset) {
-      setSelectedPresetName(preset.name);
-      // Extract filament type from preset name (e.g., "Devil Design PLA Basic @BBL X1C" -> look for PLA, PETG, etc.)
-      const typeMatch = preset.name.match(/\b(PLA|PETG|ABS|TPU|ASA|PA|PC|PVA|HIPS|PLA-S|PETG-CF|PA-CF|PET-CF)\b/i);
-      if (typeMatch) {
-        setSelectedFilamentType(typeMatch[1].toUpperCase());
-      }
-    }
-  };
-
-  // Find best K-profile when data loads or filament type changes
-  // First filter all profiles by extruder ID (for dual-nozzle printers)
-  const allProfiles = kProfilesData?.profiles || [];
-  const profiles = allProfiles.filter(p => p.extruder_id === extruderId);
-
-  // Filter profiles to show only matching ones in dropdown
-  const getMatchingProfiles = (): KProfile[] => {
-    if (!profiles.length) return [];
-
-    const subBrands = isBambuSpool
-      ? tray.tray_sub_brands?.toLowerCase()
-      : (selectedPresetName ? getCleanFilamentName(selectedPresetName).toLowerCase() : null);
-    const type = isBambuSpool
-      ? tray.tray_type?.toUpperCase()
-      : (selectedFilamentType?.toUpperCase() || tray.tray_type?.toUpperCase());
-
-    // If we have tray_sub_brands, only show profiles matching that (not generic type)
-    // For Bambu spools, also require "bambu" in the profile name to exclude third-party profiles
-    if (subBrands) {
-      const matches = profiles.filter(p => p.name.toLowerCase().includes(subBrands));
-      if (isBambuSpool) {
-        return matches.filter(p => p.name.toLowerCase().includes('bambu'));
-      }
-      return matches;
-    }
-
-    // Only fall back to type matching if no sub_brands available
-    if (type) {
-      return profiles.filter(p => p.name.toUpperCase().includes(type));
-    }
-
-    return [];
-  };
-
-  const matchingProfiles = getMatchingProfiles();
-
-  // Get color name from Bambu tray_id_name (e.g., "A00-Y2" -> "Sunflower Yellow")
-  const bambuColorName = getColorNameFromTrayId(tray.tray_id_name);
-
-  useEffect(() => {
-    // First, try to find a profile matching the tray's current K value (from printer)
-    // This preserves the user's selection when reopening the modal
-    const currentK = tray.k;
-    if (currentK && currentK > 0) {
-      const matchingKProfile = matchingProfiles.find(p => {
-        const profileK = parseFloat(p.k_value);
-        return Math.abs(profileK - currentK) < 0.0001; // Allow small floating point tolerance
-      });
-      if (matchingKProfile) {
-        setSelectedKProfile(matchingKProfile.name);
-        setKValue(currentK);
-        return;
-      }
-    }
-
-    // Fall back to name-based matching if no K-value match found
-    // For Bambu spools, use tray_sub_brands (e.g., "PLA Basic") and color name
-    // For non-Bambu, use clean name from preset (e.g., "Bambu PLA Basic" without #) for K-profile matching
-    const subBrands = isBambuSpool
-      ? tray.tray_sub_brands
-      : (selectedPresetName ? getCleanFilamentName(selectedPresetName) : null);
-    const type = isBambuSpool ? tray.tray_type : (selectedFilamentType || tray.tray_type);
-    const colorName = isBambuSpool ? bambuColorName : null;
-
-    const bestProfile = findBestKProfile(profiles, subBrands, type, colorName);
-
-    if (bestProfile) {
-      setSelectedKProfile(bestProfile.name);
-      setKValue(parseFloat(bestProfile.k_value) || 0);
-    } else {
-      setSelectedKProfile('Default');
-      setKValue(0);
-    }
-  }, [profiles, matchingProfiles, selectedFilamentType, selectedPresetName, tray.tray_type, tray.tray_sub_brands, tray.tray_id_name, tray.k, isBambuSpool, bambuColorName]);
-
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') onClose();
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose]);
-
-  const handleKProfileChange = (profileName: string) => {
-    setSelectedKProfile(profileName);
-    const profile = profiles.find(p => p.name === profileName);
-    if (profile) {
-      setKValue(parseFloat(profile.k_value) || 0);
-    }
-  };
-
-  const handleConfirm = () => {
-    console.log('[AMSMaterialsModal] handleConfirm called');
-    console.log('[AMSMaterialsModal] amsId:', amsId, 'tray.id:', tray.id, 'kValue:', kValue);
-
-    // Save preset mapping for non-Bambu slots
-    if (isEditable && selectedPresetId && selectedPresetName) {
-      console.log('[AMSMaterialsModal] Saving preset mapping');
-      savePresetMutation.mutate({
-        presetId: selectedPresetId,
-        presetName: selectedPresetName,
-      });
-    }
-
-    // Send filament settings to printer (including K value)
-    // Use current tray data for Bambu spools, or edited values for non-Bambu
-    const trayColor = isBambuSpool
-      ? (tray.tray_color || '808080FF')
-      : hexToTrayColor(selectedColor);
-    const trayType = isBambuSpool
-      ? (tray.tray_type || 'PLA')
-      : (selectedFilamentType || 'PLA');
-    const traySubBrands = isBambuSpool
-      ? (tray.tray_sub_brands || '')
-      : (selectedPresetName ? getCleanFilamentName(selectedPresetName) : '');
-
-    const payload = {
-      ams_id: amsId,
-      tray_id: tray.id,
-      tray_info_idx: tray.tray_info_idx || '',
-      tray_type: trayType,
-      tray_sub_brands: traySubBrands,
-      tray_color: trayColor,
-      nozzle_temp_min: nozzleTempMin,
-      nozzle_temp_max: nozzleTempMax,
-      k: kValue,
-    };
-    console.log('[AMSMaterialsModal] Calling saveFilamentSettingMutation with payload:', payload);
-    saveFilamentSettingMutation.mutate(payload);
-
-    onConfirm?.({
-      filamentType: selectedFilamentType,
-      color: selectedColor,
-      nozzleTempMax,
-      nozzleTempMin,
-      kProfile: selectedKProfile,
-      kValue,
-    });
-    onClose();
-  };
-
-  const handleReset = () => {
-    setSelectedFilamentType(tray.tray_type || '');
-    setSelectedColor(trayColorToHex(tray.tray_color));
-    setNozzleTempMax(tray.nozzle_temp_max || 220);
-    setNozzleTempMin(tray.nozzle_temp_min || 190);
-    setKValue(tray.k || 0);
-  };
-
-  const filamentDisplayName = isBambuSpool
-    ? (tray.tray_sub_brands || tray.tray_type || 'Unknown')
-    : (selectedFilamentType || 'Select filament');
-
-  const serialNumber = isBambuSpool && tray.tray_uuid
-    ? tray.tray_uuid
-    : 'N/A';
-
-  return (
-    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
-      <div className="bg-white dark:bg-bambu-dark-secondary rounded-lg shadow-xl max-w-md w-full">
-        {/* Header */}
-        <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-bambu-dark-tertiary">
-          <h2 className="text-lg font-semibold text-gray-900 dark:text-white">
-            AMS Materials Setting - {slotLabel}
-          </h2>
-          <button
-            onClick={onClose}
-            className="p-1 hover:bg-gray-100 dark:hover:bg-bambu-dark-tertiary rounded-lg transition-colors"
-          >
-            <X className="w-5 h-5 text-gray-500 dark:text-bambu-gray" />
-          </button>
-        </div>
-
-        {/* Content */}
-        <div className="p-6 space-y-4">
-          {/* Status indicator */}
-          {isBambuSpool && (
-            <div className="flex items-center gap-2 text-xs text-bambu-green bg-bambu-green/10 px-3 py-1.5 rounded-md">
-              <span className="w-2 h-2 bg-bambu-green rounded-full"></span>
-              Bambu Lab RFID detected - fields auto-filled
-            </div>
-          )}
-          {!isBambuSpool && !isEmpty && (
-            <div className="flex items-center gap-2 text-xs text-yellow-600 dark:text-yellow-400 bg-yellow-100 dark:bg-yellow-900/20 px-3 py-1.5 rounded-md">
-              <span className="w-2 h-2 bg-yellow-500 rounded-full"></span>
-              Non-Bambu Lab filament - please verify settings
-            </div>
-          )}
-          {isEmpty && (
-            <div className="flex items-center gap-2 text-xs text-gray-500 dark:text-bambu-gray bg-gray-100 dark:bg-bambu-dark px-3 py-1.5 rounded-md">
-              <span className="w-2 h-2 bg-gray-400 rounded-full"></span>
-              Empty slot - configure filament settings
-            </div>
-          )}
-
-          {/* Filament */}
-          <div className="flex items-center gap-4">
-            <label className="w-28 text-sm text-gray-600 dark:text-bambu-gray">Filament</label>
-            {isEditable ? (
-              <select
-                value={selectedPresetId || ''}
-                onChange={(e) => handlePresetSelect(e.target.value)}
-                className="flex-1 min-w-0 px-3 py-2 bg-gray-100 dark:bg-bambu-dark rounded-md border border-gray-300 dark:border-bambu-dark-tertiary text-gray-900 dark:text-white text-sm truncate"
-              >
-                <option value="">Select filament...</option>
-                {filamentPresets.length > 0 ? (
-                  filamentPresets.map(preset => (
-                    <option key={preset.setting_id} value={preset.setting_id}>
-                      {getBaseFilamentName(preset.name)}
-                    </option>
-                  ))
-                ) : (
-                  <option value="" disabled>No filament presets for {printerModel}</option>
-                )}
-              </select>
-            ) : (
-              <div className="flex-1 px-3 py-2 bg-gray-100 dark:bg-bambu-dark rounded-md text-gray-900 dark:text-white">
-                {filamentDisplayName}
-              </div>
-            )}
-          </div>
-
-          {/* Color */}
-          <div className="flex items-center gap-4">
-            <label className="w-28 text-sm text-gray-600 dark:text-bambu-gray">Color</label>
-            <div className="flex items-center gap-2 flex-1">
-              {isEditable ? (
-                <>
-                  <input
-                    type="color"
-                    value={selectedColor}
-                    onChange={(e) => setSelectedColor(e.target.value)}
-                    className="w-10 h-10 rounded cursor-pointer border-2 border-gray-300 dark:border-bambu-dark-tertiary"
-                  />
-                  <span className="text-gray-900 dark:text-white text-sm">{selectedColor.toUpperCase()}</span>
-                </>
-              ) : (
-                <>
-                  <div
-                    className="w-8 h-8 rounded-full border-2 border-gray-300 dark:border-bambu-dark-tertiary flex-shrink-0"
-                    style={{ backgroundColor: hexToRgb(tray.tray_color) }}
-                  />
-                  <span className="text-gray-900 dark:text-white">
-                    {bambuColorName || trayColorToHex(tray.tray_color).toUpperCase()}
-                  </span>
-                </>
-              )}
-            </div>
-          </div>
-
-          {/* Nozzle Temperature */}
-          <div className="flex items-center gap-4">
-            <label className="w-28 text-sm text-gray-600 dark:text-bambu-gray">Nozzle Temp</label>
-            <div className="flex items-center gap-2 flex-1">
-              <div>
-                <div className="text-xs text-gray-500 dark:text-bambu-gray mb-1">min</div>
-                <div className="flex items-center gap-1">
-                  <input
-                    type="number"
-                    value={nozzleTempMin}
-                    onChange={(e) => setNozzleTempMin(Number(e.target.value))}
-                    disabled={!isEditable}
-                    className="w-20 px-2 py-1 bg-gray-100 dark:bg-bambu-dark rounded border border-gray-300 dark:border-bambu-dark-tertiary text-gray-900 dark:text-white disabled:opacity-60"
-                  />
-                  <span className="text-gray-600 dark:text-bambu-gray">°C</span>
-                </div>
-              </div>
-              <div>
-                <div className="text-xs text-gray-500 dark:text-bambu-gray mb-1">max</div>
-                <div className="flex items-center gap-1">
-                  <input
-                    type="number"
-                    value={nozzleTempMax}
-                    onChange={(e) => setNozzleTempMax(Number(e.target.value))}
-                    disabled={!isEditable}
-                    className="w-20 px-2 py-1 bg-gray-100 dark:bg-bambu-dark rounded border border-gray-300 dark:border-bambu-dark-tertiary text-gray-900 dark:text-white disabled:opacity-60"
-                  />
-                  <span className="text-gray-600 dark:text-bambu-gray">°C</span>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          {/* SN */}
-          <div className="flex items-center gap-4">
-            <label className="w-28 text-sm text-gray-600 dark:text-bambu-gray">SN</label>
-            <div className="flex-1 px-3 py-2 bg-gray-100 dark:bg-bambu-dark rounded-md text-gray-900 dark:text-white text-xs font-mono truncate">
-              {serialNumber}
-            </div>
-          </div>
-
-          {/* Flow Dynamics Calibration */}
-          <div className="pt-4 border-t border-gray-200 dark:border-bambu-dark-tertiary">
-            <h3 className="text-sm font-medium text-gray-900 dark:text-white mb-3">
-              Flow Dynamics Calibration{' '}
-              <a
-                href="https://wiki.bambulab.com/en/software/bambu-studio/calibration_pa"
-                target="_blank"
-                rel="noopener noreferrer"
-                className="text-bambu-green hover:underline inline-flex items-center gap-1"
-              >
-                Wiki
-                <ExternalLink className="w-3 h-3" />
-              </a>
-            </h3>
-
-            {/* K Profile */}
-            <div className="flex items-center gap-4 mb-3">
-              <label className="w-28 text-sm text-gray-600 dark:text-bambu-gray flex-shrink-0">PA Profile</label>
-              <select
-                value={selectedKProfile}
-                onChange={(e) => handleKProfileChange(e.target.value)}
-                className="flex-1 min-w-0 px-3 py-2 bg-gray-100 dark:bg-bambu-dark rounded-md border border-gray-300 dark:border-bambu-dark-tertiary text-gray-900 dark:text-white text-sm truncate"
-              >
-                <option value="Default">Default</option>
-                {matchingProfiles.map(profile => (
-                  <option key={profile.slot_id} value={profile.name}>
-                    {profile.name} (K={parseFloat(profile.k_value).toFixed(3)})
-                  </option>
-                ))}
-              </select>
-            </div>
-
-            {/* Factor K */}
-            <div className="flex items-center gap-4">
-              <label className="w-28 text-sm text-gray-600 dark:text-bambu-gray">Factor K</label>
-              <div className="flex-1 px-3 py-2 bg-gray-100 dark:bg-bambu-dark rounded-md text-gray-900 dark:text-white">
-                {kValue.toFixed(3)}
-              </div>
-            </div>
-          </div>
-        </div>
-
-        {/* Footer */}
-        <div className="flex items-center justify-center gap-3 p-4 border-t border-gray-200 dark:border-bambu-dark-tertiary">
-          <button
-            onClick={handleConfirm}
-            className="px-6 py-2 bg-bambu-green text-white rounded-md hover:bg-bambu-green-dark transition-colors"
-          >
-            Confirm
-          </button>
-          <button
-            onClick={handleReset}
-            className="px-6 py-2 bg-gray-200 dark:bg-bambu-dark text-gray-700 dark:text-bambu-gray rounded-md hover:bg-gray-300 dark:hover:bg-bambu-dark-tertiary transition-colors"
-          >
-            Reset
-          </button>
-          <button
-            onClick={onClose}
-            className="px-6 py-2 bg-gray-200 dark:bg-bambu-dark text-gray-700 dark:text-bambu-gray rounded-md hover:bg-gray-300 dark:hover:bg-bambu-dark-tertiary transition-colors"
-          >
-            Close
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}

+ 0 - 192
frontend/src/components/control/AMSPanel.tsx

@@ -1,192 +0,0 @@
-import { useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { PrinterStatus, AMSUnit } from '../../api/client';
-import { Package, Loader2, ArrowDown, ArrowUp, Droplets, Thermometer } from 'lucide-react';
-
-interface AMSPanelProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-function hexToRgb(hex: string | null): string {
-  if (!hex) return 'rgb(128, 128, 128)';
-  // Handle RRGGBBAA format
-  const cleanHex = hex.replace('#', '').substring(0, 6);
-  const rParsed = parseInt(cleanHex.substring(0, 2), 16);
-  const gParsed = parseInt(cleanHex.substring(2, 4), 16);
-  const bParsed = parseInt(cleanHex.substring(4, 6), 16);
-  const r = isNaN(rParsed) ? 128 : rParsed;
-  const g = isNaN(gParsed) ? 128 : gParsed;
-  const b = isNaN(bParsed) ? 128 : bParsed;
-  return `rgb(${r}, ${g}, ${b})`;
-}
-
-export function AMSPanel({ printerId, status }: AMSPanelProps) {
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING';
-  const amsUnits: AMSUnit[] = status?.ams ?? [];
-
-  const [selectedTray, setSelectedTray] = useState<number | null>(null);
-
-  const loadMutation = useMutation({
-    mutationFn: (trayId: number) => api.amsLoadFilament(printerId, trayId),
-  });
-
-  const unloadMutation = useMutation({
-    mutationFn: () => api.amsUnloadFilament(printerId),
-  });
-
-  const handleLoad = () => {
-    if (selectedTray !== null) {
-      loadMutation.mutate(selectedTray);
-    }
-  };
-
-  const handleUnload = () => {
-    unloadMutation.mutate();
-  };
-
-  const isLoading = loadMutation.isPending || unloadMutation.isPending;
-
-  if (amsUnits.length === 0) {
-    return (
-      <div className="bg-bambu-dark-secondary rounded-lg p-4">
-        <div className="flex items-center gap-2 mb-4">
-          <Package className="w-4 h-4 text-bambu-gray" />
-          <h3 className="text-sm font-medium">AMS</h3>
-        </div>
-        <p className="text-sm text-bambu-gray text-center py-4">
-          No AMS detected or using external spool
-        </p>
-      </div>
-    );
-  }
-
-  return (
-    <div className="bg-bambu-dark-secondary rounded-lg p-4">
-      <div className="flex items-center justify-between mb-4">
-        <div className="flex items-center gap-2">
-          <Package className="w-4 h-4 text-bambu-gray" />
-          <h3 className="text-sm font-medium">AMS</h3>
-        </div>
-        {isLoading && <Loader2 className="w-4 h-4 animate-spin text-bambu-green" />}
-      </div>
-
-      {/* AMS Units */}
-      {amsUnits.map((unit) => (
-        <div key={unit.id} className="mb-4">
-          <div className="flex items-center justify-between text-xs text-bambu-gray mb-2">
-            <span>AMS {unit.id + 1}</span>
-            <div className="flex items-center gap-3">
-              {unit.humidity !== null && (
-                <span className="flex items-center gap-1">
-                  <Droplets className="w-3 h-3" />
-                  {unit.humidity}%
-                </span>
-              )}
-              {unit.temp !== null && (
-                <span className="flex items-center gap-1">
-                  <Thermometer className="w-3 h-3" />
-                  {unit.temp}°C
-                </span>
-              )}
-            </div>
-          </div>
-          <div className="grid grid-cols-4 gap-2">
-            {unit.tray.map((tray) => {
-              const globalTrayId = unit.id * 4 + tray.id;
-              const isSelected = selectedTray === globalTrayId;
-              const isEmpty = !tray.tray_type || tray.tray_type === '' || tray.tray_type === 'NONE';
-
-              return (
-                <button
-                  key={tray.id}
-                  onClick={() => !isEmpty && setSelectedTray(isSelected ? null : globalTrayId)}
-                  disabled={isEmpty || isPrinting}
-                  className={`relative p-2 rounded-lg transition-all ${
-                    isSelected
-                      ? 'ring-2 ring-bambu-green bg-bambu-dark'
-                      : 'bg-bambu-dark hover:bg-bambu-dark-tertiary'
-                  } ${isEmpty ? 'opacity-50' : ''} disabled:cursor-not-allowed`}
-                >
-                  {/* Color Indicator */}
-                  <div
-                    className="w-8 h-8 mx-auto rounded-full mb-1 border-2 border-bambu-dark-tertiary"
-                    style={{
-                      backgroundColor: isEmpty ? '#333' : hexToRgb(tray.tray_color),
-                    }}
-                  />
-
-                  {/* Tray Number */}
-                  <div className="text-xs text-center text-bambu-gray">
-                    {tray.id + 1}
-                  </div>
-
-                  {/* Type */}
-                  <div className="text-xs text-center truncate" title={tray.tray_type ?? ''}>
-                    {isEmpty ? '--' : tray.tray_type}
-                  </div>
-
-                  {/* Remaining */}
-                  {!isEmpty && (
-                    <div className="mt-1">
-                      <div className="h-1 bg-bambu-dark-tertiary rounded-full overflow-hidden">
-                        <div
-                          className="h-full bg-bambu-green"
-                          style={{ width: `${Math.min(100, tray.remain)}%` }}
-                        />
-                      </div>
-                      <div className="text-[10px] text-center text-bambu-gray mt-0.5">
-                        {tray.remain}%
-                      </div>
-                    </div>
-                  )}
-                </button>
-              );
-            })}
-          </div>
-        </div>
-      ))}
-
-      {/* Load/Unload Controls */}
-      <div className="flex gap-2 mt-4">
-        <button
-          onClick={handleLoad}
-          disabled={!isConnected || isPrinting || selectedTray === null || isLoading}
-          className="flex-1 flex items-center justify-center gap-2 px-4 py-2 rounded bg-bambu-green/20 text-bambu-green hover:bg-bambu-green/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-        >
-          <ArrowDown className="w-4 h-4" />
-          <span className="text-sm">Load</span>
-        </button>
-        <button
-          onClick={handleUnload}
-          disabled={!isConnected || isPrinting || isLoading}
-          className="flex-1 flex items-center justify-center gap-2 px-4 py-2 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-        >
-          <ArrowUp className="w-4 h-4" />
-          <span className="text-sm">Unload</span>
-        </button>
-      </div>
-
-      {selectedTray !== null && (
-        <p className="mt-2 text-xs text-bambu-gray text-center">
-          Selected: Slot {(selectedTray % 4) + 1}
-          {amsUnits.length > 1 && ` (AMS ${Math.floor(selectedTray / 4) + 1})`}
-        </p>
-      )}
-
-      {isPrinting && (
-        <p className="mt-2 text-xs text-yellow-500 text-center">
-          Filament change disabled during print
-        </p>
-      )}
-
-      {(loadMutation.error || unloadMutation.error) && (
-        <p className="mt-2 text-sm text-red-400">
-          {(loadMutation.error || unloadMutation.error)?.message}
-        </p>
-      )}
-    </div>
-  );
-}

+ 0 - 1326
frontend/src/components/control/AMSSectionDual.tsx

@@ -1,1326 +0,0 @@
-import { useState, useEffect, useRef } from 'react';
-import { useQuery } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { PrinterStatus, AMSUnit, AMSTray, KProfile } from '../../api/client';
-import { Loader2, ChevronDown, ChevronUp, RotateCw } from 'lucide-react';
-import { AMSHumidityModal } from './AMSHumidityModal';
-import { AMSMaterialsModal } from './AMSMaterialsModal';
-import { useToast } from '../../contexts/ToastContext';
-import { useAmsOperations } from './useAmsOperations';
-
-
-interface AMSSectionDualProps {
-  printerId: number;
-  printerModel: string;
-  status: PrinterStatus | null | undefined;
-  nozzleCount: number;
-}
-
-function hexToRgb(hex: string | null): string {
-  if (!hex) return 'rgb(128, 128, 128)';
-  const cleanHex = hex.replace('#', '').substring(0, 6);
-  const rParsed = parseInt(cleanHex.substring(0, 2), 16);
-  const gParsed = parseInt(cleanHex.substring(2, 4), 16);
-  const bParsed = parseInt(cleanHex.substring(4, 6), 16);
-  const r = isNaN(rParsed) ? 128 : rParsed;
-  const g = isNaN(gParsed) ? 128 : gParsed;
-  const b = isNaN(bParsed) ? 128 : bParsed;
-  return `rgb(${r}, ${g}, ${b})`;
-}
-
-function isLightColor(hex: string | null): boolean {
-  if (!hex) return false;
-  const cleanHex = hex.replace('#', '').substring(0, 6);
-  // Ensure we have a valid 6-char hex
-  if (cleanHex.length < 6) return false;
-  const rParsed = parseInt(cleanHex.substring(0, 2), 16);
-  const gParsed = parseInt(cleanHex.substring(2, 4), 16);
-  const bParsed = parseInt(cleanHex.substring(4, 6), 16);
-  // If any parsing fails, treat as dark
-  if (isNaN(rParsed) || isNaN(gParsed) || isNaN(bParsed)) return false;
-  // Use relative luminance formula (WCAG)
-  const luminance = (0.299 * rParsed + 0.587 * gParsed + 0.114 * bParsed) / 255;
-  // Lower threshold (0.45) to ensure more colors get white text for better contrast
-  return luminance > 0.45;
-}
-
-// Bambu Lab color codes from tray_id_name (e.g., "A00-Y2" -> "Sunflower Yellow")
-const BAMBU_COLOR_CODES: Record<string, string> = {
-  'Y2': 'Sunflower Yellow', 'Y0': 'Yellow', 'Y1': 'Lemon Yellow',
-  'K0': 'Black', 'W0': 'White', 'W1': 'Ivory White',
-  'R0': 'Red', 'R1': 'Scarlet Red', 'R2': 'Magenta',
-  'B0': 'Blue', 'B1': 'Navy Blue', 'B2': 'Sky Blue', 'B3': 'Cyan',
-  'G0': 'Green', 'G1': 'Grass Green', 'G2': 'Jade Green',
-  'O0': 'Orange', 'O1': 'Mandarin Orange',
-  'P0': 'Purple', 'P1': 'Pink', 'P2': 'Sakura Pink',
-  'N0': 'Gray', 'N1': 'Silver Gray', 'N2': 'Charcoal',
-  'D0': 'Brown', 'D1': 'Chocolate',
-  'T0': 'Titan Gray', 'T1': 'Jade White',
-};
-
-function getColorNameFromTrayId(trayIdName: string | null): string | null {
-  if (!trayIdName) return null;
-  // tray_id_name format: "A00-Y2" or "G02-K0" - color code is after the dash
-  const parts = trayIdName.split('-');
-  if (parts.length < 2) return null;
-  const colorCode = parts[1];
-  return BAMBU_COLOR_CODES[colorCode] || null;
-}
-
-// Find best matching K-profile for a filament using cascading search
-// Priority: 1) tray_sub_brands + color, 2) tray_sub_brands only, 3) tray_type only, 4) default
-function findBestKProfile(
-  profiles: KProfile[],
-  traySubBrands: string | null,
-  trayType: string | null,
-  colorName: string | null
-): KProfile | null {
-  if (!profiles.length) return null;
-
-  const subBrands = traySubBrands?.toLowerCase() || '';
-  const type = trayType?.toUpperCase() || '';
-  const color = colorName?.toLowerCase() || '';
-
-  // Priority 1: Match tray_sub_brands AND color (e.g., "PLA Basic" + "Sunflower Yellow")
-  if (subBrands && color) {
-    const exactColorMatch = profiles.find(p => {
-      const name = p.name.toLowerCase();
-      return name.includes(subBrands) && name.includes(color);
-    });
-    if (exactColorMatch) return exactColorMatch;
-  }
-
-  // Priority 2: Match tray_sub_brands without color (e.g., "PLA Basic" in "High Flow_Bambu PLA Basic")
-  if (subBrands) {
-    const subBrandsMatch = profiles.find(p =>
-      p.name.toLowerCase().includes(subBrands)
-    );
-    if (subBrandsMatch) return subBrandsMatch;
-  }
-
-  // Priority 3: Match filament type in profile name (e.g., "PLA" in "High Flow_Bambu PLA Basic")
-  if (type) {
-    const typeMatches = profiles.filter(p =>
-      p.name.toUpperCase().includes(type)
-    );
-
-    if (typeMatches.length > 0) {
-      // Prefer "Basic" profiles for generic type matching
-      const basicMatch = typeMatches.find(p =>
-        p.name.toLowerCase().includes('basic')
-      );
-      if (basicMatch) return basicMatch;
-
-      return typeMatches[0];
-    }
-  }
-
-  // Priority 4: Default profile
-  const defaultProfile = profiles.find(p =>
-    p.name.toLowerCase() === 'default' || p.slot_id === 0
-  );
-  return defaultProfile || null;
-}
-
-// Single humidity icon that fills based on level
-// <25% = empty (dry/good)
-// <40% = half filled
-// >=40% = full (wet/bad)
-function HumidityIcon({ humidity }: { humidity: number }) {
-  const getIconSrc = (): string => {
-    if (humidity < 25) return '/icons/humidity-empty.svg';
-    if (humidity < 40) return '/icons/humidity-half.svg';
-    return '/icons/humidity-full.svg';
-  };
-
-  return (
-    <img
-      src={getIconSrc()}
-      alt=""
-      className="w-2.5 h-[14px]"
-    />
-  );
-}
-
-// Filament change progress card - appears during load/unload operations
-interface FilamentChangeCardProps {
-  isLoading: boolean;  // true = loading, false = unloading
-  amsStatusMain: number;  // AMS status: 0=idle, 1=filament_change, 2=rfid_identifying, etc.
-  amsStatusSub: number;  // ams_status_sub from MQTT - step within filament change operation
-  trayNow: number;  // Currently loaded tray (255 = none, 254 = external)
-  targetTrayId: number | null;  // Target tray we're trying to load (null for unload)
-  onComplete: () => void;  // Called when operation completes
-  onRetry?: () => void;
-  operationInitiated: boolean;  // True if we initiated the operation (hook is in LOADING/UNLOADING state)
-}
-
-interface StepInfo {
-  label: string;
-  status: 'completed' | 'in_progress' | 'pending';
-  stepNumber: number;
-}
-
-function FilamentChangeCard({ isLoading, amsStatusMain, amsStatusSub, trayNow, targetTrayId, onComplete, onRetry, operationInitiated }: FilamentChangeCardProps) {
-  const [isCollapsed, setIsCollapsed] = useState(false);
-  const [isCompleted, setIsCompleted] = useState(false);
-  const prevAmsStatusRef = useRef(amsStatusMain);
-  const prevTrayNowRef = useRef(trayNow);
-  const completionTimeoutRef = useRef<NodeJS.Timeout | null>(null);
-
-  // ams_status_sub values for filament change steps
-  // Observed from H2D printer logs:
-  // Load progression: 9 (feeding start) -> 5 (prep) -> 6 (pushing) -> 2 (heating) -> 6 (more pushing) -> 7 (purging)
-  // Unload progression: 9 (start) -> 2 (heating) -> 3 (retract prep) -> 4 (retract)
-  // 2: Heating nozzle
-  // 3: AMS feeding filament to hub / retract prep
-  // 4: Retraction / extruder pulling filament
-  // 5: Initial filament push / preparation
-  // 6: Load verification / extruder pushing
-  // 7: Purging
-  // 9: Feeding start / operation initiated
-  const SUB_HEATING = 2;
-  const SUB_FEEDING = 3;
-  const SUB_RETRACT = 4;
-  const SUB_PUSH_PREP = 5;
-  const SUB_PUSH = 6;
-  const SUB_PURGE = 7;
-  const SUB_FEEDING_START = 9;
-
-  // Log status updates for debugging
-  useEffect(() => {
-    console.log(`[FilamentChangeCard] Status: main=${amsStatusMain}, sub=${amsStatusSub}, trayNow=${trayNow}, isLoading=${isLoading}`);
-  }, [amsStatusMain, amsStatusSub, trayNow, isLoading]);
-
-  // Track component mount time for minimum operation duration check
-  const mountTimeRef = useRef(Date.now());
-
-  // Detect completion via ams_status_main transition from 1 (filament_change) to 0 (idle)
-  // Also use tray_now as a secondary indicator
-  useEffect(() => {
-    const wasActive = prevAmsStatusRef.current === 1;
-    const isNowIdle = amsStatusMain === 0;
-    const trayChanged = trayNow !== prevTrayNowRef.current;
-    const elapsed = Date.now() - mountTimeRef.current;
-
-    // Don't detect completion in the first 3 seconds - operation can't complete that fast
-    // This prevents false positives from initial state or rapid updates
-    if (elapsed < 3000) {
-      prevAmsStatusRef.current = amsStatusMain;
-      prevTrayNowRef.current = trayNow;
-      return;
-    }
-
-    // Primary completion detection: ams_status_main transitions from 1 to 0
-    if (wasActive && isNowIdle) {
-      console.log(`[FilamentChangeCard] ams_status_main transitioned 1->0, operation complete!`);
-      setIsCompleted(true);
-      // Auto-close after brief delay
-      completionTimeoutRef.current = setTimeout(() => {
-        onComplete();
-      }, 1500);
-    }
-    // Secondary completion detection: tray_now matches target (for load) or becomes 255 (for unload)
-    else if (trayChanged) {
-      if (isLoading && targetTrayId !== null && trayNow === targetTrayId) {
-        console.log(`[FilamentChangeCard] Load completed! tray_now=${trayNow} matches target=${targetTrayId}`);
-        setIsCompleted(true);
-        completionTimeoutRef.current = setTimeout(() => {
-          onComplete();
-        }, 1500);
-      } else if (!isLoading && trayNow === 255) {
-        console.log(`[FilamentChangeCard] Unload completed! tray_now=255 (no filament)`);
-        setIsCompleted(true);
-        completionTimeoutRef.current = setTimeout(() => {
-          onComplete();
-        }, 1500);
-      }
-    }
-
-    prevAmsStatusRef.current = amsStatusMain;
-    prevTrayNowRef.current = trayNow;
-  }, [amsStatusMain, trayNow, isLoading, targetTrayId, onComplete]);
-
-  // Cleanup timeout on unmount
-  useEffect(() => {
-    return () => {
-      if (completionTimeoutRef.current) {
-        clearTimeout(completionTimeoutRef.current);
-      }
-    };
-  }, []);
-
-  // Determine step based on ams_status_sub from MQTT
-  // ams_status_sub values indicate the current step in filament change:
-  // Load sequence (actual order): 3/6 (push) -> 2 (heating) -> 7 (purging)
-  // Unload sequence: 2 (heating) -> 4 (retracting)
-  const getStepFromAmsStatusSub = (): number => {
-    if (isCompleted) return 99; // All done
-
-    // If not in filament change mode yet, check if we initiated the operation
-    // This handles the delay between sending the command and MQTT status updating
-    if (amsStatusMain !== 1) {
-      // If we initiated the operation, show step 1 as in progress
-      if (operationInitiated) return 1;
-      return 0; // Not started
-    }
-
-    if (isLoading) {
-      // Loading sequence: Push -> Heat -> Purge (matches Bambu Studio/OrcaSlicer display)
-      // Observed progression: 9 (start) -> 5 (prep) -> 6 (push) -> 2 (heat) -> 6 (push) -> 7 (purge)
-      // Map sub status to steps: 9/5/6 -> step 1, 2 -> step 2, 7 -> step 3
-      if (amsStatusSub === SUB_FEEDING_START || amsStatusSub === SUB_PUSH_PREP || amsStatusSub === SUB_PUSH || amsStatusSub === SUB_FEEDING) return 1; // Step 1: Pushing
-      if (amsStatusSub === SUB_HEATING) return 2; // Step 2: Heating
-      if (amsStatusSub === SUB_PURGE) return 3; // Step 3: Purging
-      // Default to step 1 when in filament_change mode
-      return 1;
-    } else {
-      // Unloading sequence: Heat -> Retract
-      // Observed progression: 9 (start) -> 2 (heat) -> 3 (retract prep) -> 4 (retract)
-      // Map sub status to steps: 9/2 -> step 1, 3/4 -> step 2
-      if (amsStatusSub === SUB_FEEDING_START || amsStatusSub === SUB_HEATING) return 1; // Step 1: Heating
-      if (amsStatusSub === SUB_FEEDING || amsStatusSub === SUB_RETRACT) return 2; // Step 2: Retracting
-      // Default to step 1 when in filament_change mode
-      return 1;
-    }
-  };
-
-  // Get current step from ams_status_sub
-  const currentStep = getStepFromAmsStatusSub();
-
-  // Debug: log step calculation
-  console.log(`[FilamentChangeCard] Step calculation: currentStep=${currentStep}, amsStatusSub=${amsStatusSub}`);
-
-  // Determine step status based on ams_status_sub
-  const getLoadingSteps = (): StepInfo[] => {
-    // Loading sequence: Push -> Heat -> Purge (matches Bambu Studio/OrcaSlicer display order)
-    // ams_status_sub progression: 3/6 (push) -> 2 (heating) -> 7 (purging)
-    let step1Status: 'completed' | 'in_progress' | 'pending' = 'pending';
-    let step2Status: 'completed' | 'in_progress' | 'pending' = 'pending';
-    let step3Status: 'completed' | 'in_progress' | 'pending' = 'pending';
-
-    if (currentStep >= 99) {
-      // All completed
-      step1Status = 'completed';
-      step2Status = 'completed';
-      step3Status = 'completed';
-    } else if (currentStep >= 3) {
-      // Purging - steps 1 & 2 done, step 3 active
-      step1Status = 'completed';
-      step2Status = 'completed';
-      step3Status = 'in_progress';
-    } else if (currentStep >= 2) {
-      // Heating - step 1 done, step 2 active
-      step1Status = 'completed';
-      step2Status = 'in_progress';
-    } else if (currentStep >= 1) {
-      // Pushing - step 1 active
-      step1Status = 'in_progress';
-    }
-
-    return [
-      { label: 'Push new filament into extruder', stepNumber: 1, status: step1Status },
-      { label: 'Heat the nozzle', stepNumber: 2, status: step2Status },
-      { label: 'Purge old filament', stepNumber: 3, status: step3Status },
-    ];
-  };
-
-  const getUnloadingSteps = (): StepInfo[] => {
-    let step1Status: 'completed' | 'in_progress' | 'pending' = 'pending';
-    let step2Status: 'completed' | 'in_progress' | 'pending' = 'pending';
-
-    if (currentStep >= 99) {
-      // All completed
-      step1Status = 'completed';
-      step2Status = 'completed';
-    } else if (currentStep >= 2) {
-      // Retracting - step 1 done, step 2 active
-      step1Status = 'completed';
-      step2Status = 'in_progress';
-    } else if (currentStep >= 1) {
-      // Heating - step 1 active
-      step1Status = 'in_progress';
-    }
-
-    return [
-      { label: 'Heat the nozzle', stepNumber: 1, status: step1Status },
-      { label: 'Retract filament from extruder', stepNumber: 2, status: step2Status },
-    ];
-  };
-
-  const steps = isLoading ? getLoadingSteps() : getUnloadingSteps();
-  const title = isLoading ? 'Loading' : 'Unloading';
-  const headerText = isLoading ? 'Filament loading...' : 'Filament unloading...';
-
-  return (
-    <div className="mt-3 border-l-4 border-bambu-green bg-white dark:bg-bambu-dark-secondary rounded-r-lg overflow-hidden">
-      {/* Collapsible header */}
-      <button
-        onClick={() => setIsCollapsed(!isCollapsed)}
-        className="w-full flex items-center justify-between px-4 py-2 text-bambu-green hover:bg-gray-50 dark:hover:bg-bambu-dark-tertiary transition-colors"
-      >
-        <span className="text-sm font-medium">{headerText}</span>
-        {isCollapsed ? <ChevronDown className="w-4 h-4" /> : <ChevronUp className="w-4 h-4" />}
-      </button>
-
-      {/* Content */}
-      {!isCollapsed && (
-        <div className="px-4 pb-4">
-          <div className="flex gap-6">
-            {/* Steps list */}
-            <div className="flex-1">
-              <h3 className="text-bambu-green font-semibold mb-3">{title}</h3>
-              <div className="space-y-2">
-                {steps.map((step) => (
-                  <div key={step.stepNumber} className="flex items-center gap-2">
-                    {/* Step indicator */}
-                    {step.status === 'completed' ? (
-                      <div className="w-5 h-5 rounded-full bg-bambu-green flex items-center justify-center flex-shrink-0">
-                        <svg className="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
-                        </svg>
-                      </div>
-                    ) : step.status === 'in_progress' ? (
-                      <div className="w-5 h-5 rounded-full bg-bambu-green flex items-center justify-center flex-shrink-0">
-                        <span className="text-white text-xs font-bold">{step.stepNumber}</span>
-                      </div>
-                    ) : (
-                      <div className="w-5 h-5 rounded-full border-2 border-gray-400 dark:border-gray-500 flex items-center justify-center flex-shrink-0">
-                        <span className="text-gray-400 dark:text-gray-500 text-xs font-medium">{step.stepNumber}</span>
-                      </div>
-                    )}
-                    {/* Step label */}
-                    <span className={`text-sm ${
-                      step.status === 'in_progress' ? 'text-gray-900 dark:text-white font-semibold' :
-                      step.status === 'completed' ? 'text-gray-500 dark:text-gray-400' : 'text-gray-400 dark:text-gray-500'
-                    }`}>
-                      {step.label}
-                    </span>
-                  </div>
-                ))}
-              </div>
-            </div>
-
-            {/* Extruder image */}
-            <div className="flex-shrink-0">
-              <img
-                src="/icons/extruder-change-filament.png"
-                alt="Extruder"
-                className="w-[150px] h-auto"
-              />
-            </div>
-          </div>
-
-          {/* Retry button */}
-          {onRetry && (
-            <button
-              onClick={onRetry}
-              className="mt-3 px-4 py-1.5 border border-bambu-gray rounded-full text-sm text-bambu-gray hover:bg-bambu-dark-tertiary transition-colors flex items-center gap-1.5"
-            >
-              <RotateCw className="w-3.5 h-3.5" />
-              Retry
-            </button>
-          )}
-        </div>
-      )}
-    </div>
-  );
-}
-
-interface AMSPanelContentProps {
-  units: AMSUnit[];
-  side: 'left' | 'right';
-  isPrinting: boolean;
-  selectedAmsIndex: number;
-  onSelectAms: (index: number) => void;
-  selectedTray: number | null;
-  onSelectTray: (trayId: number | null) => void;
-  onHumidityClick: (humidity: number, temp: number) => void;
-  onSlotRefresh: (amsId: number, slotId: number) => void;
-  onEyeClick: (tray: AMSTray, slotLabel: string, amsId: number) => void;
-  refreshingSlot: { amsId: number; trayId: number } | null;
-  getKValue: (tray: AMSTray) => number | null;
-  kProfilesLoaded: boolean;
-}
-
-// Panel content - NO wiring, just slots and info
-// Get slot label based on AMS unit ID and tray index
-// Regular AMS (ID 0-3): A1, A2, A3, A4 / B1, B2, B3, B4 / etc.
-// AMS-HT (ID >= 128): HT-A, HT-B (for first HT unit), HT2-A, HT2-B (for second), etc.
-function getSlotLabel(amsId: number, trayIndex: number): string {
-  if (amsId >= 128) {
-    // AMS-HT unit - uses HT-A, HT-B naming
-    const htUnitNumber = amsId - 128; // 0 for first HT, 1 for second, etc.
-    const slotLetter = String.fromCharCode(65 + trayIndex); // A, B
-    if (htUnitNumber === 0) {
-      return `HT-${slotLetter}`;
-    }
-    return `HT${htUnitNumber + 1}-${slotLetter}`;
-  }
-  // Regular AMS - uses A1, B2, etc. naming
-  const prefix = String.fromCharCode(65 + amsId); // 65 is ASCII for 'A'
-  return `${prefix}${trayIndex + 1}`;
-}
-
-// Check if AMS unit is an AMS-HT (ID >= 128)
-function isAmsHT(amsId: number): boolean {
-  return amsId >= 128;
-}
-
-function AMSPanelContent({
-  units,
-  side,
-  isPrinting,
-  selectedAmsIndex,
-  onSelectAms,
-  selectedTray,
-  onSelectTray,
-  onHumidityClick,
-  onSlotRefresh,
-  onEyeClick,
-  refreshingSlot,
-  getKValue,
-  kProfilesLoaded,
-}: AMSPanelContentProps) {
-  const selectedUnit = units[selectedAmsIndex];
-  const isHT = selectedUnit ? isAmsHT(selectedUnit.id) : false;
-
-  return (
-    <div className="flex-1 min-w-0">
-      {/* AMS Tab Selectors */}
-      <div className="flex gap-1.5 mb-2.5 p-1.5 bg-gray-300 dark:bg-bambu-dark rounded-lg">
-        {units.map((unit, index) => (
-          <button
-            key={unit.id}
-            onClick={() => onSelectAms(index)}
-            className={`flex items-center p-1.5 rounded border-2 transition-colors ${
-              selectedAmsIndex === index
-                ? 'border-bambu-green bg-white dark:bg-bambu-dark-tertiary'
-                : 'bg-gray-200 dark:bg-bambu-dark-secondary border-transparent hover:border-bambu-gray'
-            }`}
-          >
-            <div className="flex gap-0.5">
-              {unit.tray.map((tray) => (
-                <div
-                  key={tray.id}
-                  className="w-2.5 h-2.5 rounded-full"
-                  style={{
-                    backgroundColor: tray.tray_color ? hexToRgb(tray.tray_color) : '#808080',
-                  }}
-                />
-              ))}
-            </div>
-          </button>
-        ))}
-      </div>
-
-      {/* AMS Content */}
-      {selectedUnit && (
-        <div className="bg-gray-100 dark:bg-bambu-dark-secondary rounded-[10px] p-2.5">
-          {/* AMS Header - Humidity & Temp - Centered - Clickable */}
-          <button
-            onClick={() => onHumidityClick(selectedUnit.humidity ?? 0, selectedUnit.temp ?? 0)}
-            className="flex items-center justify-center gap-4 text-xs text-bambu-gray mb-2.5 w-full py-1 hover:bg-gray-50 dark:hover:bg-bambu-dark-tertiary rounded-md transition-colors cursor-pointer"
-          >
-            {selectedUnit.humidity !== null && (
-              <span className="flex items-center gap-1.5">
-                <HumidityIcon humidity={selectedUnit.humidity} />
-                {selectedUnit.humidity} %
-              </span>
-            )}
-            {selectedUnit.temp !== null && (
-              <span className="flex items-center gap-1.5">
-                <img src="/icons/temperature.svg" alt="" className="w-3.5 icon-theme" />
-                {selectedUnit.temp}°C
-              </span>
-            )}
-          </button>
-
-          {/* Slot Labels */}
-          <div className={`flex gap-2 mb-1.5 ${isHT ? 'justify-start pl-2' : 'justify-center'}`}>
-            {selectedUnit.tray.map((tray, index) => {
-              const slotLabel = getSlotLabel(selectedUnit.id, index);
-              const isRefreshing = refreshingSlot?.amsId === selectedUnit.id && refreshingSlot?.trayId === tray.id;
-              return (
-                <button
-                  key={tray.id}
-                  onClick={(e) => {
-                    e.stopPropagation();
-                    onSlotRefresh(selectedUnit.id, tray.id);
-                  }}
-                  disabled={isRefreshing}
-                  className={`w-14 flex items-center justify-center gap-0.5 text-[10px] text-bambu-gray px-1.5 py-[3px] bg-bambu-dark rounded-full border border-bambu-dark-tertiary transition-colors ${
-                    isRefreshing ? 'opacity-70 cursor-wait' : 'hover:bg-bambu-dark-tertiary'
-                  }`}
-                >
-                  {slotLabel}
-                  <img
-                    src="/icons/reload.svg"
-                    alt=""
-                    className={`w-2.5 h-2.5 icon-theme ${isRefreshing ? 'animate-spin' : ''}`}
-                  />
-                </button>
-              );
-            })}
-          </div>
-
-          {/* AMS Slots - NO wiring here */}
-          <div className={`flex gap-2 ${isHT ? 'justify-start pl-2' : 'justify-center'}`}>
-            {selectedUnit.tray.map((tray, index) => {
-              const globalTrayId = selectedUnit.id * 4 + tray.id;
-              const isSelected = selectedTray === globalTrayId;
-              const isEmpty = !tray.tray_type || tray.tray_type === '' || tray.tray_type === 'NONE';
-              const isLight = isLightColor(tray.tray_color);
-              const slotLabel = getSlotLabel(selectedUnit.id, index);
-
-              return (
-                <div
-                  key={tray.id}
-                  onClick={() => {
-                    console.log(`[AMSSectionDual] Slot clicked: AMS ${selectedUnit.id}, tray ${tray.id}, globalTrayId: ${globalTrayId}, isEmpty: ${isEmpty}, isPrinting: ${isPrinting}, isSelected: ${isSelected}`);
-                    if (!isEmpty && !isPrinting) {
-                      onSelectTray(isSelected ? null : globalTrayId);
-                    }
-                  }}
-                  className={`w-14 h-[80px] rounded-md border-2 overflow-hidden transition-all bg-bambu-dark relative ${
-                    isSelected
-                      ? 'border-bambu-green'
-                      : 'border-bambu-dark-tertiary hover:border-bambu-gray'
-                  } ${isEmpty ? 'opacity-50' : 'cursor-pointer'}`}
-                >
-                  {/* Fill level indicator - show for any filament with valid remain data */}
-                  {!isEmpty && tray.remain >= 0 && (
-                    <div
-                      className="absolute bottom-0 left-0 right-0 transition-all"
-                      style={{
-                        height: `${Math.min(100, Math.max(0, tray.remain))}%`,
-                        backgroundColor: hexToRgb(tray.tray_color),
-                      }}
-                    />
-                  )}
-                  {/* Full color background only when no valid remain data */}
-                  {!isEmpty && tray.remain < 0 && (
-                    <div
-                      className="absolute inset-0"
-                      style={{
-                        backgroundColor: hexToRgb(tray.tray_color),
-                      }}
-                    />
-                  )}
-                  {/* Striped pattern for empty slots */}
-                  {isEmpty && (
-                    <div
-                      className="absolute inset-0"
-                      style={{
-                        background: 'repeating-linear-gradient(45deg, #3a3a3a, #3a3a3a 4px, #4a4a4a 4px, #4a4a4a 8px)',
-                      }}
-                    />
-                  )}
-                  {/* Content overlay */}
-                  <div className="relative w-full h-full flex flex-col items-center justify-end pb-[5px]">
-                    <span
-                      className="text-[11px] font-semibold"
-                      style={{ color: isLight ? '#000000' : '#ffffff' }}
-                    >
-                      {isEmpty ? '--' : tray.tray_type}
-                    </span>
-                    {!isEmpty && kProfilesLoaded && (
-                      <span
-                        className="text-[10px] font-medium"
-                        style={{ color: isLight ? 'rgba(0,0,0,0.8)' : 'rgba(255,255,255,0.85)' }}
-                      >
-                        K {(getKValue(tray) ?? 0.020).toFixed(3)}
-                      </span>
-                    )}
-                    {!isEmpty && (
-                      <button
-                        onClick={(e) => {
-                          e.stopPropagation();
-                          onEyeClick(tray, slotLabel, selectedUnit.id);
-                        }}
-                        className={`w-4 h-4 flex items-center justify-center rounded hover:bg-black/20 transition-colors`}
-                      >
-                        <img
-                          src="/icons/eye.svg"
-                          alt="Settings"
-                          className={`w-3.5 h-3.5 ${isLight ? '' : 'invert'}`}
-                          style={{ opacity: 0.8 }}
-                        />
-                      </button>
-                    )}
-                  </div>
-                </div>
-              );
-            })}
-          </div>
-        </div>
-      )}
-
-      {/* No AMS message */}
-      {units.length === 0 && (
-        <div className="bg-bambu-dark-secondary rounded-[10px] p-6 text-center text-bambu-gray text-sm">
-          No AMS connected to {side} nozzle
-        </div>
-      )}
-    </div>
-  );
-}
-
-// Unified wiring layer - draws ALL wiring in one place
-interface WiringLayerProps {
-  isDualNozzle: boolean;
-  leftSlotCount: number;  // Number of slots on left panel (4 for regular AMS, 1-2 for AMS-HT)
-  rightSlotCount: number; // Number of slots on right panel
-  leftIsHT: boolean;      // Is left panel an AMS-HT
-  rightIsHT: boolean;     // Is right panel an AMS-HT
-  leftActiveSlot?: number | null;   // Currently active slot index on left panel (0-3)
-  rightActiveSlot?: number | null;  // Currently active slot index on right panel (0-3)
-  leftFilamentColor?: string | null;  // Filament color for left active path
-  rightFilamentColor?: string | null; // Filament color for right active path
-}
-
-function WiringLayer({
-  isDualNozzle,
-  leftSlotCount,
-  rightSlotCount,
-  leftIsHT,
-  rightIsHT,
-  leftActiveSlot,
-  rightActiveSlot,
-  leftFilamentColor,
-  rightFilamentColor,
-}: WiringLayerProps) {
-  if (!isDualNozzle) return null;
-
-  // All measurements relative to this container
-  // Container spans full width between panels
-  // Regular AMS: slots → hub → down → toward center → down to extruder
-  // AMS-HT: single slot on left → direct line down to extruder
-
-  // Regular AMS: Slots are w-14 (56px) with gap-2 (8px), 4 slots = 248px total, centered in each ~300px panel
-  // Left panel center ~150, slots start at 150 - 124 = 26
-  // Slot centers: 26+28=54, 54+64=118, 118+64=182, 182+64=246
-
-  // AMS-HT: Left aligned with pl-2 (8px), slot starts at 8px + 28px = 36px center
-  // For 2 slots: 36, 100 (36 + 64)
-
-  // Right panel calculations for regular AMS:
-  // Right panel center ~450, slots start at 450 - 124 = 326
-  // Slot centers: 326+28=354, 354+64=418, 418+64=482, 482+64=546
-
-  // Right panel AMS-HT: Left aligned, starts at ~308 (300 panel offset + 8px padding)
-  // Slot center: 308 + 28 = 336
-
-  // Determine colors for wiring paths
-  const defaultColor = '#909090';
-  const leftActiveColor = leftFilamentColor ? hexToRgb(leftFilamentColor) : null;
-  const rightActiveColor = rightFilamentColor ? hexToRgb(rightFilamentColor) : null;
-
-  // Slot X positions for regular AMS (4 slots)
-  const leftSlotX = [54, 118, 182, 246];
-  // Right slot positions
-  const rightSlotX = [354, 418, 482, 546];
-
-  return (
-    <div className="relative w-full pointer-events-none" style={{ height: '120px' }}>
-      <svg
-        className="absolute inset-0 w-full h-full"
-        viewBox="0 0 600 120"
-        preserveAspectRatio="xMidYMid meet"
-      >
-        {/* Left panel wiring */}
-        {leftIsHT ? (
-          <>
-            {/* AMS-HT: Simple direct line from slot to extruder */}
-            {/* Slot vertical lines - highlight active slot */}
-            <line x1="36" y1="0" x2="36" y2="36" stroke={leftActiveSlot === 0 && leftActiveColor ? leftActiveColor : defaultColor} strokeWidth={leftActiveSlot === 0 && leftActiveColor ? 3 : 2} />
-            {leftSlotCount > 1 && (
-              <line x1="100" y1="0" x2="100" y2="36" stroke={leftActiveSlot === 1 && leftActiveColor ? leftActiveColor : defaultColor} strokeWidth={leftActiveSlot === 1 && leftActiveColor ? 3 : 2} />
-            )}
-            {leftSlotCount > 1 && (
-              <line x1="36" y1="36" x2="100" y2="36" stroke={leftActiveColor ?? defaultColor} strokeWidth={leftActiveColor ? 3 : 2} />
-            )}
-            {/* Path to extruder - always colored if filament loaded */}
-            <line x1={leftSlotCount > 1 ? "68" : "36"} y1="36" x2="288" y2="36" stroke={leftActiveColor ?? defaultColor} strokeWidth={leftActiveColor ? 3 : 2} />
-            <line x1="288" y1="36" x2="288" y2="85" stroke={leftActiveColor ?? defaultColor} strokeWidth={leftActiveColor ? 3 : 2} />
-          </>
-        ) : (
-          <>
-            {/* Regular AMS: 4 slots with hub */}
-            {/* Vertical lines from 4 slots - highlight active slot */}
-            {leftSlotX.map((x, i) => (
-              <line key={`left-slot-${i}`} x1={x} y1="0" x2={x} y2="14" stroke={leftActiveSlot === i && leftActiveColor ? leftActiveColor : defaultColor} strokeWidth={leftActiveSlot === i && leftActiveColor ? 3 : 2} />
-            ))}
-
-            {/* Horizontal bar connecting left slots - highlight from active slot to hub */}
-            {leftActiveSlot !== null && leftActiveSlot !== undefined && leftActiveColor ? (
-              <>
-                {/* Background bar */}
-                <line x1="54" y1="14" x2="246" y2="14" stroke={defaultColor} strokeWidth="2" />
-                {/* Highlight segment from active slot to hub (center at 150) */}
-                <line
-                  x1={Math.min(leftSlotX[leftActiveSlot], 150)}
-                  y1="14"
-                  x2={Math.max(leftSlotX[leftActiveSlot], 150)}
-                  y2="14"
-                  stroke={leftActiveColor}
-                  strokeWidth="3"
-                />
-              </>
-            ) : (
-              <line x1="54" y1="14" x2="246" y2="14" stroke={defaultColor} strokeWidth="2" />
-            )}
-
-            {/* Left hub */}
-            <rect x="136" y="8" width="28" height="14" rx="2" fill={leftActiveColor ?? '#c0c0c0'} stroke={leftActiveColor ?? defaultColor} strokeWidth="1" />
-
-            {/* Vertical from left hub down */}
-            <line x1="150" y1="22" x2="150" y2="36" stroke={leftActiveColor ?? defaultColor} strokeWidth={leftActiveColor ? 3 : 2} />
-
-            {/* Horizontal from left hub toward center */}
-            <line x1="150" y1="36" x2="288" y2="36" stroke={leftActiveColor ?? defaultColor} strokeWidth={leftActiveColor ? 3 : 2} />
-
-            {/* Vertical down to left extruder inlet */}
-            <line x1="288" y1="36" x2="288" y2="85" stroke={leftActiveColor ?? defaultColor} strokeWidth={leftActiveColor ? 3 : 2} />
-          </>
-        )}
-
-        {/* Right panel wiring */}
-        {rightIsHT ? (
-          <>
-            {/* AMS-HT: Simple direct line from slot to extruder */}
-            <line x1="336" y1="0" x2="336" y2="36" stroke={rightActiveSlot === 0 && rightActiveColor ? rightActiveColor : defaultColor} strokeWidth={rightActiveSlot === 0 && rightActiveColor ? 3 : 2} />
-            {rightSlotCount > 1 && (
-              <line x1="400" y1="0" x2="400" y2="36" stroke={rightActiveSlot === 1 && rightActiveColor ? rightActiveColor : defaultColor} strokeWidth={rightActiveSlot === 1 && rightActiveColor ? 3 : 2} />
-            )}
-            {rightSlotCount > 1 && (
-              <line x1="336" y1="36" x2="400" y2="36" stroke={rightActiveColor ?? defaultColor} strokeWidth={rightActiveColor ? 3 : 2} />
-            )}
-            <line x1="312" y1="36" x2={rightSlotCount > 1 ? "368" : "336"} y2="36" stroke={rightActiveColor ?? defaultColor} strokeWidth={rightActiveColor ? 3 : 2} />
-            <line x1="312" y1="36" x2="312" y2="85" stroke={rightActiveColor ?? defaultColor} strokeWidth={rightActiveColor ? 3 : 2} />
-          </>
-        ) : (
-          <>
-            {/* Regular AMS: 4 slots with hub */}
-            {/* Vertical lines from 4 slots - highlight active slot */}
-            {rightSlotX.map((x, i) => (
-              <line key={`right-slot-${i}`} x1={x} y1="0" x2={x} y2="14" stroke={rightActiveSlot === i && rightActiveColor ? rightActiveColor : defaultColor} strokeWidth={rightActiveSlot === i && rightActiveColor ? 3 : 2} />
-            ))}
-
-            {/* Horizontal bar connecting right slots - highlight from active slot to hub */}
-            {rightActiveSlot !== null && rightActiveSlot !== undefined && rightActiveColor ? (
-              <>
-                {/* Background bar */}
-                <line x1="354" y1="14" x2="546" y2="14" stroke={defaultColor} strokeWidth="2" />
-                {/* Highlight segment from active slot to hub (center at 450) */}
-                <line
-                  x1={Math.min(rightSlotX[rightActiveSlot], 450)}
-                  y1="14"
-                  x2={Math.max(rightSlotX[rightActiveSlot], 450)}
-                  y2="14"
-                  stroke={rightActiveColor}
-                  strokeWidth="3"
-                />
-              </>
-            ) : (
-              <line x1="354" y1="14" x2="546" y2="14" stroke={defaultColor} strokeWidth="2" />
-            )}
-
-            {/* Right hub */}
-            <rect x="436" y="8" width="28" height="14" rx="2" fill={rightActiveColor ?? '#c0c0c0'} stroke={rightActiveColor ?? defaultColor} strokeWidth="1" />
-
-            {/* Vertical from right hub down */}
-            <line x1="450" y1="22" x2="450" y2="36" stroke={rightActiveColor ?? defaultColor} strokeWidth={rightActiveColor ? 3 : 2} />
-
-            {/* Horizontal from right hub toward center */}
-            <line x1="312" y1="36" x2="450" y2="36" stroke={rightActiveColor ?? defaultColor} strokeWidth={rightActiveColor ? 3 : 2} />
-
-            {/* Vertical down to right extruder inlet */}
-            <line x1="312" y1="36" x2="312" y2="85" stroke={rightActiveColor ?? defaultColor} strokeWidth={rightActiveColor ? 3 : 2} />
-          </>
-        )}
-      </svg>
-
-      {/* Extruder image container - positioned at bottom center */}
-      {/* Image is 56x71 pixels, scaled to h=50px = width ~39px */}
-      {/* Scale factor: 50/71 = 0.704 */}
-      {/* Green circles in original image: left center ~(15.2,34.2), right center ~(41.0,33.9) */}
-      {/* Scaled positions: left x≈10.7, right x≈28.9, y≈24 from top */}
-      <div className="absolute left-1/2 -translate-x-1/2 bottom-0 h-[50px] w-[39px]">
-        <img
-          src="/icons/extruder-left-right.png"
-          alt="Extruder"
-          className="h-full w-full"
-        />
-        {/* Extruder inlet indicator circles - overlay on extruder image */}
-        {/* Left inlet (extruder 1) - left side of extruder */}
-        <div
-          className="absolute w-[8px] h-[8px] rounded-full"
-          style={{
-            left: '7px',
-            top: '20px',
-            backgroundColor: leftActiveColor ?? 'transparent',
-          }}
-        />
-        {/* Right inlet (extruder 0) - right side of extruder */}
-        <div
-          className="absolute w-[8px] h-[8px] rounded-full"
-          style={{
-            left: '25px',
-            top: '20px',
-            backgroundColor: rightActiveColor ?? 'transparent',
-          }}
-        />
-      </div>
-    </div>
-  );
-}
-
-export function AMSSectionDual({ printerId, printerModel, status, nozzleCount }: AMSSectionDualProps) {
-  const { showToast } = useToast();
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING';
-  const isDualNozzle = nozzleCount > 1;
-  const amsUnits: AMSUnit[] = status?.ams ?? [];
-  // Per-AMS extruder map: {ams_id: extruder_id} where extruder 0=right, 1=left
-  // This is extracted from each AMS unit's info field bit 8 in the backend
-  // Note: JSON keys are always strings, so we use Record<string, number>
-  const amsExtruderMap: Record<string, number> = status?.ams_extruder_map ?? {};
-
-  // Get nozzle diameter for K-profile lookup (default to 0.4)
-  const nozzleDiameter = status?.nozzles?.[0]?.nozzle_diameter || '0.4';
-
-  // Fetch K-profiles for this printer
-  const { data: kProfilesData, isFetched: kProfilesFetched } = useQuery({
-    queryKey: ['kprofiles', printerId, nozzleDiameter],
-    queryFn: () => api.getKProfiles(printerId, nozzleDiameter),
-    enabled: isConnected,
-    staleTime: 30000, // Cache for 30 seconds to avoid flicker on re-renders
-  });
-
-  // Create K-value lookup using cascading search by tray properties
-  // Priority: 1) tray_sub_brands + color, 2) tray_sub_brands only, 3) tray_type only, 4) default
-  const getKValueForSlot = (tray: AMSTray): number | null => {
-    const profiles = kProfilesData?.profiles || [];
-    if (!profiles.length) return null;
-
-    // Get color name from tray_id_name (e.g., "A00-Y2" -> "Sunflower Yellow")
-    const colorName = getColorNameFromTrayId(tray.tray_id_name || null);
-
-    // Use cascading search to find best matching K-profile
-    const matchedProfile = findBestKProfile(
-      profiles,
-      tray.tray_sub_brands || null,
-      tray.tray_type || null,
-      colorName
-    );
-
-    if (matchedProfile) {
-      return parseFloat(matchedProfile.k_value) || null;
-    }
-    return null; // No profile found for this filament
-  };
-
-  // Distribute AMS units based on ams_extruder_map
-  // Each AMS unit's info field tells us which extruder it's connected to:
-  // Bambu convention: extruder_id 0 = RIGHT physical nozzle, extruder_id 1 = LEFT physical nozzle
-  // UI layout: Left panel shows extruder 1 (left nozzle), Right panel shows extruder 0 (right nozzle)
-  const leftUnits = (() => {
-    if (!isDualNozzle) return amsUnits;
-    if (Object.keys(amsExtruderMap).length > 0) {
-      // Filter AMS units assigned to extruder 1 (LEFT physical nozzle -> left UI panel)
-      return amsUnits.filter(unit => amsExtruderMap[String(unit.id)] === 1);
-    }
-    // Fallback: even indices go to left
-    return amsUnits.filter((_, i) => i % 2 === 0);
-  })();
-
-  const rightUnits = (() => {
-    if (!isDualNozzle) return [];
-    if (Object.keys(amsExtruderMap).length > 0) {
-      // Filter AMS units assigned to extruder 0 (RIGHT physical nozzle -> right UI panel)
-      return amsUnits.filter(unit => amsExtruderMap[String(unit.id)] === 0);
-    }
-    // Fallback: odd indices go to right
-    return amsUnits.filter((_, i) => i % 2 === 1);
-  })();
-
-  const [leftAmsIndex, setLeftAmsIndex] = useState(0);
-  const [rightAmsIndex, setRightAmsIndex] = useState(0);
-  const [selectedTray, setSelectedTray] = useState<number | null>(null);
-
-  // Modal states
-  const [humidityModal, setHumidityModal] = useState<{ humidity: number; temp: number } | null>(null);
-  const [materialsModal, setMaterialsModal] = useState<{ tray: AMSTray; slotLabel: string; amsId: number } | null>(null);
-
-  // Get ams_status values from printer status
-  const amsStatusMain = status?.ams_status_main ?? 0;
-  const trayNow = status?.tray_now ?? 255;
-  const lastAmsUpdate = status?.last_ams_update ?? 0;
-
-  // AMS Operations hook - manages state machine for refresh/load/unload
-  const amsOps = useAmsOperations({
-    printerId,
-    amsUnits,
-    amsStatusMain,
-    trayNow,
-    lastAmsUpdate,
-    onToast: showToast,
-  });
-
-  // Track if we've done initial sync from tray_now
-  const initialSyncDone = useRef(false);
-
-  // Sync selectedTray from status.tray_now on initial load
-  // tray_now: 255 = no filament loaded, 0-253 = valid tray ID, 254 = external spool
-  useEffect(() => {
-    if (initialSyncDone.current) return;
-
-    const trayNow = status?.tray_now;
-    if (trayNow !== undefined && trayNow !== null) {
-      initialSyncDone.current = true;
-      if (trayNow !== 255 && trayNow !== 254) {
-        // Valid AMS tray is loaded - select it
-        // Note: We don't set loadTriggered here because the user may want to load a different slot
-        console.log(`[AMSSectionDual] Initializing from tray_now: ${trayNow}`);
-        setSelectedTray(trayNow);
-      } else {
-        // No filament loaded or external spool
-        console.log(`[AMSSectionDual] tray_now=${trayNow} (no AMS filament loaded)`);
-      }
-    }
-  }, [status?.tray_now]);
-
-
-
-  // Handle tray selection
-  const handleTraySelect = (trayId: number | null) => {
-    setSelectedTray(trayId);
-  };
-
-  // Helper to get extruder ID for a given tray
-  const getExtruderIdForTray = (trayId: number): number | undefined => {
-    // For dual-nozzle printers, calculate which AMS unit the tray belongs to
-    // and look up which extruder it's connected to
-    if (!isDualNozzle) return undefined;
-
-    // Find which AMS unit contains this tray
-    // Global tray ID format: amsId * 4 + slotIndex (for regular AMS)
-    // For AMS-HT (id >= 128): amsId * 4 + slotIndex (but only 2 slots)
-    for (const unit of amsUnits) {
-      const slotsInUnit = unit.id >= 128 ? 2 : 4; // AMS-HT has 2 slots
-      const baseSlotId = unit.id * 4;
-      if (trayId >= baseSlotId && trayId < baseSlotId + slotsInUnit) {
-        // Found the AMS unit - look up its extruder
-        const extruderId = amsExtruderMap[String(unit.id)];
-        console.log(`[AMSSectionDual] Tray ${trayId} belongs to AMS ${unit.id}, extruder: ${extruderId}`);
-        return extruderId;
-      }
-    }
-    return undefined;
-  };
-
-  const handleLoad = () => {
-    console.log(`[AMSSectionDual] handleLoad called, selectedTray: ${selectedTray}`);
-    if (selectedTray !== null) {
-      const extruderId = getExtruderIdForTray(selectedTray);
-      console.log(`[AMSSectionDual] Starting load via hook: tray ${selectedTray}, extruder ${extruderId}`);
-      amsOps.startLoad(selectedTray, extruderId);
-    }
-  };
-
-  const handleUnload = () => {
-    console.log(`[AMSSectionDual] handleUnload called, printerId: ${printerId}, trayNow: ${trayNow}`);
-    amsOps.startUnload();
-  };
-
-  // Callback for FilamentChangeCard to close itself
-  const handleFilamentChangeComplete = () => {
-    console.log(`[AMSSectionDual] FilamentChangeCard completed, resetting operation`);
-    amsOps.reset();
-  };
-
-  // Handlers for modals and actions
-  const handleHumidityClick = (humidity: number, temp: number) => {
-    setHumidityModal({ humidity, temp });
-  };
-
-  const handleSlotRefresh = (amsId: number, slotId: number) => {
-    console.log(`[AMSSectionDual] Slot refresh triggered: AMS ${amsId}, Slot ${slotId}`);
-    amsOps.startRefresh(amsId, slotId);
-  };
-
-  const handleEyeClick = (tray: AMSTray, slotLabel: string, amsId: number) => {
-    setMaterialsModal({ tray, slotLabel, amsId });
-  };
-
-  // Show FilamentChangeCard when operation is in progress (LOADING or UNLOADING state)
-  // Use debounced visibility to prevent flickering when ams_status_main bounces
-  const isMqttFilamentChangeActive = amsStatusMain === 1;
-  const shouldShowCard = amsOps.state === 'LOADING' || amsOps.state === 'UNLOADING' || isMqttFilamentChangeActive;
-
-  // Debounce hiding the card - keep it visible for at least 2 seconds after conditions become false
-  const [showFilamentChangeCard, setShowFilamentChangeCard] = useState(false);
-  const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null);
-
-  useEffect(() => {
-    if (shouldShowCard) {
-      // Clear any pending hide timeout
-      if (hideTimeoutRef.current) {
-        clearTimeout(hideTimeoutRef.current);
-        hideTimeoutRef.current = null;
-      }
-      setShowFilamentChangeCard(true);
-    } else if (showFilamentChangeCard) {
-      // Delay hiding the card by 1.5 seconds to prevent flicker
-      if (!hideTimeoutRef.current) {
-        hideTimeoutRef.current = setTimeout(() => {
-          setShowFilamentChangeCard(false);
-          hideTimeoutRef.current = null;
-        }, 1500);
-      }
-    }
-
-    return () => {
-      if (hideTimeoutRef.current) {
-        clearTimeout(hideTimeoutRef.current);
-      }
-    };
-  }, [shouldShowCard, showFilamentChangeCard]);
-
-  // Determine if it's a load operation for the FilamentChangeCard
-  // Simple logic: use state if active, otherwise use lastOperationType
-  // - LOADING state -> definitely load
-  // - UNLOADING state -> definitely unload
-  // - IDLE state -> use lastOperationType to remember what we initiated
-  // - Fallback: if lastOperationType is null and MQTT shows activity, infer from tray_now
-  //   (tray_now=255 means unloading, any other value means loading)
-  const isFilamentLoadOperation = (() => {
-    if (amsOps.state === 'LOADING') return true;
-    if (amsOps.state === 'UNLOADING') return false;
-    if (amsOps.lastOperationType === 'load') return true;
-    if (amsOps.lastOperationType === 'unload') return false;
-    // Fallback: infer from tray_now when MQTT shows filament change active
-    // If tray has a valid filament (not 255), we're loading
-    return trayNow !== 255;
-  })();
-
-  // Debug logging for card type determination
-  useEffect(() => {
-    if (showFilamentChangeCard) {
-      console.log(`[AMSSectionDual] Card type: isFilamentLoadOperation=${isFilamentLoadOperation}, state=${amsOps.state}, lastOperationType=${amsOps.lastOperationType}, trayNow=${trayNow}`);
-    }
-  }, [showFilamentChangeCard, isFilamentLoadOperation, amsOps.state, amsOps.lastOperationType, trayNow]);
-
-  // Get the loaded tray info for wire coloring and extruder inlet
-  // Wire coloring: only show path if the currently displayed AMS panel has the loaded filament
-  // Extruder inlet: ALWAYS show the loaded filament color regardless of which AMS is displayed
-  const getLoadedTrayInfo = (): {
-    leftActiveSlot: number | null;
-    rightActiveSlot: number | null;
-    leftFilamentColor: string | null;
-    rightFilamentColor: string | null;
-  } => {
-    // tray_now: 255 = no filament, 254 = external spool, 0-253 = valid tray ID
-    if (trayNow === 255 || trayNow === 254) {
-      return { leftActiveSlot: null, rightActiveSlot: null, leftFilamentColor: null, rightFilamentColor: null };
-    }
-
-    // Find which AMS and slot contains the loaded tray
-    for (const unit of amsUnits) {
-      const slotsInUnit = unit.id >= 128 ? 2 : 4;
-      const baseSlotId = unit.id * 4;
-      if (trayNow >= baseSlotId && trayNow < baseSlotId + slotsInUnit) {
-        const slotIndex = trayNow - baseSlotId;
-        const tray = unit.tray[slotIndex];
-        const color = tray?.tray_color ?? null;
-
-        // Determine if this AMS is on left or right UI panel
-        // Bambu convention: extruder 0 = RIGHT physical nozzle, extruder 1 = LEFT physical nozzle
-        // UI layout: left panel shows extruder 1 (left nozzle), right panel shows extruder 0 (right nozzle)
-        const extruderId = amsExtruderMap[String(unit.id)];
-
-        // Check if this AMS unit is the one currently displayed in the panel
-        const currentLeftUnit = leftUnits[leftAmsIndex];
-        const currentRightUnit = rightUnits[rightAmsIndex];
-
-        if (extruderId === 1) {
-          // Left UI panel (extruder 1 = LEFT physical nozzle) - leftUnits filters for amsExtruderMap === 1
-          // Wire path: only show if the currently displayed AMS unit is the one with loaded filament
-          // Extruder inlet: ALWAYS show the loaded filament color
-          const isDisplayed = currentLeftUnit?.id === unit.id;
-          return {
-            leftActiveSlot: isDisplayed ? slotIndex : null,  // Wire path only when AMS is displayed
-            rightActiveSlot: null,
-            leftFilamentColor: color,  // Extruder inlet always shows loaded color
-            rightFilamentColor: null
-          };
-        } else {
-          // Right UI panel (extruder 0 = RIGHT physical nozzle) - rightUnits filters for amsExtruderMap === 0
-          const isDisplayed = currentRightUnit?.id === unit.id;
-          return {
-            leftActiveSlot: null,
-            rightActiveSlot: isDisplayed ? slotIndex : null,  // Wire path only when AMS is displayed
-            leftFilamentColor: null,
-            rightFilamentColor: color  // Extruder inlet always shows loaded color
-          };
-        }
-      }
-    }
-
-    return { leftActiveSlot: null, rightActiveSlot: null, leftFilamentColor: null, rightFilamentColor: null };
-  };
-
-  const { leftActiveSlot, rightActiveSlot, leftFilamentColor, rightFilamentColor } = getLoadedTrayInfo();
-
-  // Create refreshingSlot object from hook state for spinner visibility
-  const refreshingSlot = amsOps.state === 'REFRESHING' && amsOps.context?.refreshTarget
-    ? { amsId: amsOps.context.refreshTarget.amsId, trayId: amsOps.context.refreshTarget.trayId }
-    : null;
-
-  return (
-    <div className="bg-bambu-dark-tertiary rounded-[10px] p-3">
-      {/* Dual Panel Layout - just the panels, no wiring */}
-      <div className="flex gap-5">
-        <AMSPanelContent
-          units={leftUnits}
-          side="left"
-          isPrinting={isPrinting}
-          selectedAmsIndex={leftAmsIndex}
-          onSelectAms={setLeftAmsIndex}
-          selectedTray={selectedTray}
-          onSelectTray={handleTraySelect}
-          onHumidityClick={handleHumidityClick}
-          onSlotRefresh={handleSlotRefresh}
-          onEyeClick={handleEyeClick}
-          refreshingSlot={refreshingSlot}
-          getKValue={getKValueForSlot}
-          kProfilesLoaded={kProfilesFetched}
-        />
-
-        {isDualNozzle && (
-          <AMSPanelContent
-            units={rightUnits}
-            side="right"
-            isPrinting={isPrinting}
-            selectedAmsIndex={rightAmsIndex}
-            onSelectAms={setRightAmsIndex}
-            selectedTray={selectedTray}
-            onSelectTray={handleTraySelect}
-            onHumidityClick={handleHumidityClick}
-            onSlotRefresh={handleSlotRefresh}
-            onEyeClick={handleEyeClick}
-            refreshingSlot={refreshingSlot}
-            getKValue={getKValueForSlot}
-            kProfilesLoaded={kProfilesFetched}
-          />
-        )}
-      </div>
-
-      {/* Unified Wiring Layer - ALL wiring drawn here */}
-      <WiringLayer
-        isDualNozzle={isDualNozzle}
-        leftSlotCount={leftUnits[leftAmsIndex]?.tray?.length ?? 4}
-        rightSlotCount={rightUnits[rightAmsIndex]?.tray?.length ?? 4}
-        leftIsHT={leftUnits[leftAmsIndex] ? isAmsHT(leftUnits[leftAmsIndex].id) : false}
-        rightIsHT={rightUnits[rightAmsIndex] ? isAmsHT(rightUnits[rightAmsIndex].id) : false}
-        leftActiveSlot={leftActiveSlot}
-        rightActiveSlot={rightActiveSlot}
-        leftFilamentColor={leftFilamentColor}
-        rightFilamentColor={rightFilamentColor}
-      />
-
-      {/* Action Buttons Row - aligned with extruder */}
-      <div className="flex items-start -mt-[50px]">
-        <div className="flex items-center gap-2">
-          <button className="w-10 h-10 rounded-lg bg-bambu-dark-secondary hover:bg-bambu-dark border border-bambu-dark-tertiary flex items-center justify-center">
-            <img src="/icons/ams-settings.svg" alt="Settings" className="w-5 icon-theme" />
-          </button>
-          <button className="px-[18px] py-2.5 rounded-lg bg-bambu-dark-secondary hover:bg-bambu-dark border border-bambu-dark-tertiary text-sm text-bambu-gray flex items-center gap-1.5">
-            Auto-refill
-          </button>
-        </div>
-
-        <div className="flex-1" />
-
-        <div className="flex items-center gap-2">
-          {/* Unload button: disabled if not connected, printing, operation in progress, or no filament loaded */}
-          <button
-            onClick={handleUnload}
-            disabled={!isConnected || isPrinting || amsOps.isOperationInProgress || trayNow === 255}
-            className={`px-7 py-2.5 rounded-lg text-sm transition-colors border ${
-              !isConnected || isPrinting || amsOps.isOperationInProgress || trayNow === 255
-                ? 'bg-bambu-gray-dark text-gray-500 border-bambu-gray-dark cursor-not-allowed'
-                : 'bg-bambu-dark-secondary text-white border-bambu-dark-tertiary hover:bg-bambu-dark'
-            }`}
-          >
-            {amsOps.state === 'UNLOADING' ? (
-              <Loader2 className="w-4 h-4 animate-spin" />
-            ) : (
-              'Unload'
-            )}
-          </button>
-          {/* Load button: disabled if not connected, printing, operation in progress, no tray selected, or selected tray is already loaded */}
-          <button
-            onClick={handleLoad}
-            disabled={!isConnected || isPrinting || selectedTray === null || amsOps.isOperationInProgress || selectedTray === trayNow}
-            className={`px-7 py-2.5 rounded-lg text-sm transition-colors border ${
-              !isConnected || isPrinting || selectedTray === null || amsOps.isOperationInProgress || selectedTray === trayNow
-                ? 'bg-bambu-gray-dark text-gray-500 border-bambu-gray-dark cursor-not-allowed'
-                : 'bg-bambu-dark-secondary text-white border-bambu-dark-tertiary hover:bg-bambu-dark'
-            }`}
-          >
-            {amsOps.state === 'LOADING' ? (
-              <Loader2 className="w-4 h-4 animate-spin" />
-            ) : (
-              'Load'
-            )}
-          </button>
-        </div>
-      </div>
-
-      {/* Error messages */}
-      {(amsOps.loadError || amsOps.unloadError) && (
-        <p className="mt-2 text-sm text-red-500 text-center">
-          {(amsOps.loadError || amsOps.unloadError)?.message}
-        </p>
-      )}
-
-      {/* Filament Change Progress Card - appears during load/unload operations */}
-      {showFilamentChangeCard && (
-        <FilamentChangeCard
-          isLoading={isFilamentLoadOperation}
-          amsStatusMain={amsStatusMain}
-          amsStatusSub={status?.ams_status_sub ?? 0}
-          trayNow={trayNow}
-          targetTrayId={amsOps.loadTargetTrayId}
-          onComplete={handleFilamentChangeComplete}
-          operationInitiated={amsOps.state === 'LOADING' || amsOps.state === 'UNLOADING'}
-        />
-      )}
-
-      {/* Humidity Modal */}
-      {humidityModal && (
-        <AMSHumidityModal
-          humidity={humidityModal.humidity}
-          temperature={humidityModal.temp}
-          dryingStatus="idle"
-          onClose={() => setHumidityModal(null)}
-        />
-      )}
-
-      {/* Materials Settings Modal */}
-      {materialsModal && (
-        <AMSMaterialsModal
-          tray={materialsModal.tray}
-          amsId={materialsModal.amsId}
-          slotLabel={materialsModal.slotLabel}
-          printerId={printerId}
-          printerModel={printerModel}
-          nozzleDiameter={status?.nozzles?.[0]?.nozzle_diameter || '0.4'}
-          extruderId={amsExtruderMap[materialsModal.amsId.toString()] ?? 0}
-          onClose={() => setMaterialsModal(null)}
-        />
-      )}
-    </div>
-  );
-}

+ 0 - 451
frontend/src/components/control/AirConditionModal.tsx

@@ -1,451 +0,0 @@
-import { useState, useEffect } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { Printer, PrinterStatus } from '../../api/client';
-import { X, Minus, Plus, Wind, Flame, AlertTriangle } from 'lucide-react';
-
-interface AirConditionModalProps {
-  printer: Printer;
-  status: PrinterStatus | null | undefined;
-  onClose: () => void;
-}
-
-type Mode = 'cooling' | 'heating';
-
-// Toggle switch component
-function Toggle({
-  checked,
-  onChange,
-  disabled,
-}: {
-  checked: boolean;
-  onChange: (checked: boolean) => void;
-  disabled?: boolean;
-}) {
-  return (
-    <button
-      onClick={() => !disabled && onChange(!checked)}
-      disabled={disabled}
-      className={`w-11 h-6 rounded-full relative transition-all shadow-inner border ${
-        checked ? 'bg-bambu-green border-bambu-green' : 'bg-bambu-dark-tertiary border-bambu-dark-tertiary'
-      } ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
-    >
-      <div
-        className={`w-5 h-5 rounded-full bg-white shadow-md absolute top-0.5 transition-transform ${
-          checked ? 'translate-x-5' : 'translate-x-0.5'
-        }`}
-      />
-    </button>
-  );
-}
-
-// Fan control card component
-function FanCard({
-  name,
-  icon,
-  speed,
-  enabled,
-  onToggle,
-  onSpeedChange,
-  disabled,
-  showSpeedControl = true,
-}: {
-  name: string;
-  icon: React.ReactNode;
-  speed: number;
-  enabled: boolean;
-  onToggle: (enabled: boolean) => void;
-  onSpeedChange: (speed: number) => void;
-  disabled?: boolean;
-  showSpeedControl?: boolean;
-}) {
-  const increment = () => {
-    const newSpeed = Math.min(100, speed + 10);
-    onSpeedChange(newSpeed);
-  };
-
-  const decrement = () => {
-    const newSpeed = Math.max(0, speed - 10);
-    onSpeedChange(newSpeed);
-  };
-
-  return (
-    <div className="bg-bambu-dark rounded-xl p-4 shadow-lg border border-bambu-dark-tertiary/50">
-      {/* Header */}
-      <div className="flex items-center justify-between mb-4">
-        <div className="flex items-center gap-2.5">
-          <div className="w-8 h-8 rounded-lg bg-bambu-dark-tertiary flex items-center justify-center">
-            {icon}
-          </div>
-          <span className="text-white font-medium">{name}</span>
-        </div>
-        {showSpeedControl && (
-          <Toggle checked={enabled} onChange={onToggle} disabled={disabled} />
-        )}
-      </div>
-
-      {/* Controls or Status */}
-      {showSpeedControl ? (
-        <div className="flex items-center justify-between bg-bambu-dark-secondary rounded-lg p-2">
-          <button
-            onClick={decrement}
-            disabled={disabled || !enabled}
-            className="w-9 h-9 rounded-lg bg-bambu-dark-tertiary flex items-center justify-center text-white hover:bg-bambu-dark transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
-          >
-            <Minus className="w-4 h-4" />
-          </button>
-          <div className="flex-1 text-center">
-            <span className={`text-xl font-semibold ${enabled ? 'text-white' : 'text-bambu-gray'}`}>
-              {speed}%
-            </span>
-          </div>
-          <button
-            onClick={increment}
-            disabled={disabled || !enabled}
-            className="w-9 h-9 rounded-lg bg-bambu-dark-tertiary flex items-center justify-center text-white hover:bg-bambu-dark transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
-          >
-            <Plus className="w-4 h-4" />
-          </button>
-        </div>
-      ) : (
-        <div className="p-3 text-center">
-          <span className={`text-xl font-semibold ${enabled ? 'text-bambu-green' : 'text-bambu-green'}`}>
-            {enabled ? 'Auto' : 'Off'}
-          </span>
-        </div>
-      )}
-    </div>
-  );
-}
-
-// Warning dialog component
-function PrintWarningDialog({
-  onConfirm,
-  onCancel,
-}: {
-  onConfirm: () => void;
-  onCancel: () => void;
-}) {
-  return (
-    <div className="absolute inset-0 bg-black/80 flex items-center justify-center z-10 rounded-2xl">
-      <div className="bg-bambu-dark-secondary rounded-xl p-5 m-4 max-w-sm border border-bambu-dark-tertiary shadow-xl">
-        <div className="flex items-center gap-3 mb-4">
-          <div className="w-10 h-10 rounded-full bg-yellow-500/20 flex items-center justify-center flex-shrink-0">
-            <AlertTriangle className="w-5 h-5 text-yellow-500" />
-          </div>
-          <div>
-            <h3 className="text-white font-semibold">Print in Progress</h3>
-            <p className="text-sm text-bambu-gray">The printer is currently controlling air conditioning.</p>
-          </div>
-        </div>
-        <p className="text-sm text-bambu-gray mb-5">
-          Changing these settings during a print may affect print quality. Are you sure you want to continue?
-        </p>
-        <div className="flex gap-3">
-          <button
-            onClick={onCancel}
-            className="flex-1 py-2.5 px-4 rounded-lg font-medium text-sm bg-bambu-dark-tertiary text-white hover:bg-bambu-dark transition-colors"
-          >
-            Cancel
-          </button>
-          <button
-            onClick={onConfirm}
-            className="flex-1 py-2.5 px-4 rounded-lg font-medium text-sm bg-yellow-600 text-white hover:bg-yellow-700 transition-colors"
-          >
-            Continue
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
-
-export function AirConditionModal({ printer, status, onClose }: AirConditionModalProps) {
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING' || status?.state === 'PRINTING';
-  const isDisabled = !isConnected;
-
-  // Initialize mode from printer status (0=cooling, 1=heating)
-  const initialMode: Mode = (status?.airduct_mode ?? 0) === 1 ? 'heating' : 'cooling';
-  const [mode, setMode] = useState<Mode>(initialMode);
-  const [showPrintWarning, setShowPrintWarning] = useState(false);
-  const [pendingAction, setPendingAction] = useState<(() => void) | null>(null);
-
-  // Fan states
-  const [partFanEnabled, setPartFanEnabled] = useState(false);
-  const [partFanSpeed, setPartFanSpeed] = useState(0);
-  const [auxFanEnabled, setAuxFanEnabled] = useState(false);
-  const [auxFanSpeed, setAuxFanSpeed] = useState(0);
-  const [exhaustFanEnabled, setExhaustFanEnabled] = useState(false);
-  const [exhaustFanSpeed, setExhaustFanSpeed] = useState(0);
-
-  // Mutation for airduct mode
-  const airductMutation = useMutation({
-    mutationFn: (newMode: Mode) => api.setAirductMode(printer.id, newMode),
-  });
-
-  // Mutations for fan control
-  const partFanMutation = useMutation({
-    mutationFn: (speed: number) => api.setPartFan(printer.id, speed),
-  });
-
-  const auxFanMutation = useMutation({
-    mutationFn: (speed: number) => api.setAuxFan(printer.id, speed),
-  });
-
-  const chamberFanMutation = useMutation({
-    mutationFn: (speed: number) => api.setChamberFan(printer.id, speed),
-  });
-
-  // Wrapper to check for print warning before executing action
-  const withPrintWarning = (action: () => void) => {
-    if (isPrinting) {
-      setPendingAction(() => action);
-      setShowPrintWarning(true);
-    } else {
-      action();
-    }
-  };
-
-  const handleWarningConfirm = () => {
-    if (pendingAction) {
-      pendingAction();
-      setPendingAction(null);
-    }
-    setShowPrintWarning(false);
-  };
-
-  const handleWarningCancel = () => {
-    setPendingAction(null);
-    setShowPrintWarning(false);
-  };
-
-  // Handle mode change
-  const handleModeChange = (newMode: Mode) => {
-    if (newMode === mode) return;
-
-    withPrintWarning(() => {
-      setMode(newMode);
-      airductMutation.mutate(newMode);
-    });
-  };
-
-  const handlePartFanToggle = (enabled: boolean) => {
-    withPrintWarning(() => {
-      setPartFanEnabled(enabled);
-      if (!enabled) {
-        setPartFanSpeed(0);
-        partFanMutation.mutate(0);
-      }
-    });
-  };
-
-  const handlePartFanSpeed = (speed: number) => {
-    withPrintWarning(() => {
-      setPartFanSpeed(speed);
-      setPartFanEnabled(speed > 0);
-      partFanMutation.mutate(speed);
-    });
-  };
-
-  const handleAuxFanToggle = (enabled: boolean) => {
-    withPrintWarning(() => {
-      setAuxFanEnabled(enabled);
-      if (!enabled) {
-        setAuxFanSpeed(0);
-        auxFanMutation.mutate(0);
-      }
-    });
-  };
-
-  const handleAuxFanSpeed = (speed: number) => {
-    withPrintWarning(() => {
-      setAuxFanSpeed(speed);
-      setAuxFanEnabled(speed > 0);
-      auxFanMutation.mutate(speed);
-    });
-  };
-
-  const handleExhaustFanToggle = (enabled: boolean) => {
-    withPrintWarning(() => {
-      setExhaustFanEnabled(enabled);
-      if (!enabled) {
-        setExhaustFanSpeed(0);
-        chamberFanMutation.mutate(0);
-      }
-    });
-  };
-
-  const handleExhaustFanSpeed = (speed: number) => {
-    withPrintWarning(() => {
-      setExhaustFanSpeed(speed);
-      setExhaustFanEnabled(speed > 0);
-      chamberFanMutation.mutate(speed);
-    });
-  };
-
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') {
-        if (showPrintWarning) {
-          handleWarningCancel();
-        } else {
-          onClose();
-        }
-      }
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose, showPrintWarning]);
-
-  const modeDescriptions = {
-    cooling: 'Suitable for PLA, PETG, TPU. Filters and cools the chamber air.',
-    heating: 'Suitable for ABS, ASA, PC, PA. Circulates and heats chamber air.',
-  };
-
-  const FanIcon = () => (
-    <img src="/icons/ventilation.svg" alt="" className="w-5 h-5 icon-theme" />
-  );
-
-  const HeatIcon = () => (
-    <img src="/icons/chamber.svg" alt="" className="w-5 h-5 icon-theme" />
-  );
-
-  return (
-    <div
-      className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4"
-      onClick={onClose}
-    >
-      <div
-        className="relative w-full max-w-lg bg-bambu-dark-secondary rounded-2xl shadow-2xl border border-bambu-dark-tertiary overflow-hidden"
-        onClick={(e) => e.stopPropagation()}
-      >
-        {/* Print Warning Overlay */}
-        {showPrintWarning && (
-          <PrintWarningDialog
-            onConfirm={handleWarningConfirm}
-            onCancel={handleWarningCancel}
-          />
-        )}
-
-        {/* Header */}
-        <div className="flex items-center justify-between px-5 py-4 bg-bambu-dark border-b border-bambu-dark-tertiary">
-          <div className="flex items-center gap-3">
-            <div className="w-8 h-8 rounded-lg bg-bambu-green/20 flex items-center justify-center">
-              <img src="/icons/ventilation.svg" alt="" className="w-5 h-5 icon-green" />
-            </div>
-            <span className="text-base font-semibold text-white">Air Condition</span>
-          </div>
-          <button
-            onClick={onClose}
-            className="w-8 h-8 rounded-lg flex items-center justify-center text-bambu-gray hover:bg-bambu-dark-tertiary hover:text-white transition-colors"
-          >
-            <X className="w-5 h-5" />
-          </button>
-        </div>
-
-        {/* Content */}
-        <div className="p-5 space-y-5">
-          {/* Print Warning Banner */}
-          {isPrinting && (
-            <div className="flex items-center gap-2 p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
-              <AlertTriangle className="w-4 h-4 text-yellow-500 flex-shrink-0" />
-              <span className="text-xs text-yellow-500">
-                Print in progress. Changes may affect print quality.
-              </span>
-            </div>
-          )}
-
-          {/* Mode Toggle */}
-          <div className="bg-bambu-dark rounded-xl p-1.5 flex gap-1.5 shadow-inner">
-            <button
-              onClick={() => handleModeChange('cooling')}
-              disabled={isDisabled || airductMutation.isPending}
-              className={`flex-1 py-3 px-4 rounded-lg flex items-center justify-center gap-2 font-medium transition-all disabled:opacity-50 ${
-                mode === 'cooling'
-                  ? 'bg-bambu-green text-white shadow-lg'
-                  : 'text-bambu-gray hover:text-white hover:bg-bambu-dark-tertiary'
-              }`}
-            >
-              <Wind className="w-4 h-4" />
-              Cooling
-            </button>
-            <button
-              onClick={() => handleModeChange('heating')}
-              disabled={isDisabled || airductMutation.isPending}
-              className={`flex-1 py-3 px-4 rounded-lg flex items-center justify-center gap-2 font-medium transition-all disabled:opacity-50 ${
-                mode === 'heating'
-                  ? 'bg-bambu-green text-white shadow-lg'
-                  : 'text-bambu-gray hover:text-white hover:bg-bambu-dark-tertiary'
-              }`}
-            >
-              <Flame className="w-4 h-4" />
-              Heating
-            </button>
-          </div>
-
-          {/* Mode Description */}
-          <p className="text-sm text-bambu-gray text-center px-2">{modeDescriptions[mode]}</p>
-
-          {/* Separator */}
-          <div className="border-t border-bambu-dark-tertiary" />
-
-          {/* Fan Controls Grid */}
-          <div className="grid grid-cols-2 gap-3">
-            {/* Part Fan - always has speed control */}
-            <FanCard
-              name="Part"
-              icon={<FanIcon />}
-              speed={partFanSpeed}
-              enabled={partFanEnabled}
-              onToggle={handlePartFanToggle}
-              onSpeedChange={handlePartFanSpeed}
-              disabled={isDisabled}
-              showSpeedControl={true}
-            />
-
-            {/* Aux Fan */}
-            <FanCard
-              name="Aux"
-              icon={<FanIcon />}
-              speed={auxFanSpeed}
-              enabled={auxFanEnabled}
-              onToggle={handleAuxFanToggle}
-              onSpeedChange={handleAuxFanSpeed}
-              disabled={isDisabled}
-              showSpeedControl={mode === 'cooling'}
-            />
-
-            {/* Exhaust Fan */}
-            <FanCard
-              name="Exhaust"
-              icon={<FanIcon />}
-              speed={exhaustFanSpeed}
-              enabled={exhaustFanEnabled}
-              onToggle={handleExhaustFanToggle}
-              onSpeedChange={handleExhaustFanSpeed}
-              disabled={isDisabled}
-              showSpeedControl={mode === 'cooling'}
-            />
-
-            {/* Heat Status */}
-            <div className="bg-bambu-dark rounded-xl p-4 shadow-lg border border-bambu-dark-tertiary/50">
-              <div className="flex items-center gap-2.5 mb-4">
-                <div className="w-8 h-8 rounded-lg bg-bambu-dark-tertiary flex items-center justify-center">
-                  <HeatIcon />
-                </div>
-                <span className="text-white font-medium">Heat</span>
-              </div>
-              <div className="p-3 text-center">
-                <span className="text-xl font-semibold text-bambu-green">
-                  {mode === 'heating' ? 'Auto' : 'Off'}
-                </span>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}

+ 0 - 100
frontend/src/components/control/BedControls.tsx

@@ -1,100 +0,0 @@
-import { useMutation } from '@tanstack/react-query';
-import { api, isConfirmationRequired } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { useState } from 'react';
-import { ConfirmModal } from '../ConfirmModal';
-
-interface BedControlsProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-  disabled?: boolean;
-}
-
-export function BedControls({ printerId, status, disabled = false }: BedControlsProps) {
-  const isConnected = (status?.connected ?? false) && !disabled;
-
-  const [confirmModal, setConfirmModal] = useState<{
-    token: string;
-    warning: string;
-    distance: number;
-  } | null>(null);
-
-  const moveMutation = useMutation({
-    mutationFn: ({ distance, token }: { distance: number; token?: string }) =>
-      api.moveAxis(printerId, 'Z', distance, 1000, token),
-    onSuccess: (result, variables) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          token: result.token,
-          warning: result.warning,
-          distance: variables.distance,
-        });
-      }
-    },
-  });
-
-  const handleMove = (distance: number) => {
-    moveMutation.mutate({ distance });
-  };
-
-  const handleConfirm = () => {
-    if (confirmModal) {
-      moveMutation.mutate({ distance: confirmModal.distance, token: confirmModal.token });
-      setConfirmModal(null);
-    }
-  };
-
-  const isDisabled = !isConnected || moveMutation.isPending;
-
-  return (
-    <>
-      <div className="flex items-center gap-2">
-        <button
-          onClick={() => handleMove(-10)}
-          disabled={isDisabled}
-          className="px-3.5 py-2 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary text-sm text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-          title="Bed up 10mm"
-        >
-          ↑10
-        </button>
-        <button
-          onClick={() => handleMove(-1)}
-          disabled={isDisabled}
-          className="px-3.5 py-2 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary text-sm text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-          title="Bed up 1mm"
-        >
-          ↑1
-        </button>
-        <span className="px-2 py-2 text-sm text-bambu-gray">Bed</span>
-        <button
-          onClick={() => handleMove(1)}
-          disabled={isDisabled}
-          className="px-3.5 py-2 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary text-sm text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-          title="Bed down 1mm"
-        >
-          ↓1
-        </button>
-        <button
-          onClick={() => handleMove(10)}
-          disabled={isDisabled}
-          className="px-3.5 py-2 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary text-sm text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-          title="Bed down 10mm"
-        >
-          ↓10
-        </button>
-      </div>
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Bed Movement"
-          message={confirmModal.warning}
-          confirmText="Continue"
-          variant="warning"
-          onConfirm={handleConfirm}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 424
frontend/src/components/control/CalibrationModal.tsx

@@ -1,424 +0,0 @@
-import { useEffect, useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { Printer, PrinterStatus } from '../../api/client';
-import { X, Loader2, AlertTriangle, Check } from 'lucide-react';
-import { Card, CardContent } from '../Card';
-
-interface CalibrationModalProps {
-  printer: Printer;
-  status: PrinterStatus | null | undefined;
-  onClose: () => void;
-}
-
-// Calibration stages that indicate active calibration
-const CALIBRATION_STAGES = new Set([1, 3, 13, 25, 39, 40, 47, 48, 50]);
-
-// Checkbox component matching Bambu Studio style
-function Checkbox({
-  checked,
-  onChange,
-  disabled,
-}: {
-  checked: boolean;
-  onChange: (checked: boolean) => void;
-  disabled?: boolean;
-}) {
-  return (
-    <button
-      onClick={() => !disabled && onChange(!checked)}
-      disabled={disabled}
-      className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-colors ${
-        checked
-          ? 'bg-bambu-green border-bambu-green'
-          : 'bg-transparent border-bambu-gray'
-      } ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
-    >
-      {checked && (
-        <svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
-          <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
-        </svg>
-      )}
-    </button>
-  );
-}
-
-// Timeline step component - matches Bambu Studio style
-function TimelineStep({
-  step,
-  name,
-  isActive,
-  isComplete,
-  isLast,
-}: {
-  step: number;
-  name: string;
-  isActive: boolean;
-  isComplete: boolean;
-  isLast: boolean;
-}) {
-  return (
-    <div className="flex items-start gap-3">
-      {/* Circle and line container */}
-      <div className="flex flex-col items-center">
-        {/* Number circle */}
-        <div
-          className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium flex-shrink-0 ${
-            isActive || isComplete
-              ? 'bg-bambu-green text-white'
-              : 'bg-bambu-green text-white'
-          }`}
-        >
-          {step}
-        </div>
-        {/* Vertical connecting line */}
-        {!isLast && (
-          <div className={`w-0.5 h-6 ${isComplete ? 'bg-bambu-green' : 'bg-bambu-gray/30'}`} />
-        )}
-      </div>
-      {/* Step name */}
-      <span
-        className={`text-sm pt-0.5 ${
-          isActive ? 'text-white font-semibold' : 'text-bambu-gray'
-        }`}
-      >
-        {name}
-      </span>
-    </div>
-  );
-}
-
-export function CalibrationModal({ printer, status, onClose }: CalibrationModalProps) {
-  const isConnected = status?.connected ?? false;
-  const isDualNozzle = printer.nozzle_count === 2;
-  const currentStage = status?.stg_cur ?? -1;
-
-  // Track if we've started calibration (to switch to progress view)
-  const [calibrationStarted, setCalibrationStarted] = useState(false);
-  // Track if we've seen the printer actually enter calibration mode
-  const [seenCalibrating, setSeenCalibrating] = useState(false);
-  // Track if calibration has completed
-  const [calibrationCompleted, setCalibrationCompleted] = useState(false);
-
-  // Calibration options state - restore from localStorage if calibration is in progress
-  const storageKey = `calibration_options_${printer.id}`;
-  const savedOptions = typeof window !== 'undefined' ? localStorage.getItem(storageKey) : null;
-  const parsedOptions = savedOptions ? JSON.parse(savedOptions) : null;
-
-  const [bedLeveling, setBedLeveling] = useState(parsedOptions?.bedLeveling ?? true);
-  const [vibration, setVibration] = useState(parsedOptions?.vibration ?? true);
-  const [motorNoise, setMotorNoise] = useState(parsedOptions?.motorNoise ?? true);
-  const [nozzleOffset, setNozzleOffset] = useState(parsedOptions?.nozzleOffset ?? isDualNozzle);
-  const [highTempHeatbed, setHighTempHeatbed] = useState(parsedOptions?.highTempHeatbed ?? false);
-  // Track if we've initialized based on calibration state
-  const [initialized, setInitialized] = useState(false);
-
-  // Detect if printer is currently calibrating
-  // Check both stg_cur being a calibration stage AND state being RUNNING
-  // (printer may keep stg_cur at last calibration stage after completion)
-  const printerState = status?.state;
-  const isCalibrating = CALIBRATION_STAGES.has(currentStage) && printerState === 'RUNNING';
-
-  // If calibration is already in progress when modal opens, set tracking state
-  // Checkbox values are preserved from localStorage
-  useEffect(() => {
-    if (!initialized && isCalibrating) {
-      setSeenCalibrating(true);
-      setCalibrationStarted(true);
-      setInitialized(true);
-    } else if (!initialized && !isCalibrating) {
-      setInitialized(true);
-    }
-  }, [initialized, isCalibrating]);
-
-  // Track when printer actually enters calibration mode
-  useEffect(() => {
-    if (isCalibrating && !seenCalibrating) {
-      setSeenCalibrating(true);
-      setCalibrationCompleted(false);
-    }
-  }, [isCalibrating, seenCalibrating]);
-
-  // Auto-detect if calibration was started externally (e.g., from touchscreen)
-  useEffect(() => {
-    if (isCalibrating && !calibrationStarted) {
-      setCalibrationStarted(true);
-    }
-  }, [isCalibrating, calibrationStarted]);
-
-  // Detect when calibration completes:
-  // - Must have seen calibration actually running (seenCalibrating is true)
-  // - Now isCalibrating is false (stg_cur left calibration stages OR state is no longer RUNNING)
-  useEffect(() => {
-    if (seenCalibrating && !isCalibrating && !calibrationCompleted) {
-      setCalibrationCompleted(true);
-    }
-  }, [seenCalibrating, isCalibrating, calibrationCompleted]);
-
-  // Reset function to allow starting a new calibration
-  const resetCalibration = () => {
-    localStorage.removeItem(storageKey);
-    setCalibrationStarted(false);
-    setSeenCalibrating(false);
-    setCalibrationCompleted(false);
-    // Reset to defaults
-    setBedLeveling(true);
-    setVibration(true);
-    setMotorNoise(true);
-    setNozzleOffset(isDualNozzle);
-    setHighTempHeatbed(false);
-  };
-
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') onClose();
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose]);
-
-  // Start calibration mutation
-  const calibrationMutation = useMutation({
-    mutationFn: () =>
-      api.startCalibration(printer.id, {
-        bed_leveling: bedLeveling,
-        vibration: vibration,
-        motor_noise: motorNoise,
-        nozzle_offset: nozzleOffset,
-        high_temp_heatbed: highTempHeatbed,
-      }),
-    onSuccess: () => {
-      // Save selected options to localStorage so they persist across modal close/open
-      localStorage.setItem(storageKey, JSON.stringify({
-        bedLeveling, vibration, motorNoise, nozzleOffset, highTempHeatbed
-      }));
-      setCalibrationStarted(true);
-    },
-  });
-
-  const hasSelection = bedLeveling || vibration || motorNoise || nozzleOffset || highTempHeatbed;
-  const canStart = isConnected && hasSelection && !calibrationMutation.isPending && !isCalibrating && !calibrationCompleted;
-
-  // Build expected calibration flow based on selections
-  // These are in the typical order the printer performs them
-  const expectedFlow: { name: string; stages: number[] }[] = [];
-  expectedFlow.push({ name: 'Homing toolhead', stages: [13] });
-  if (bedLeveling || highTempHeatbed) {
-    expectedFlow.push({ name: 'Cooling heatbed', stages: [50] });
-  }
-  if (bedLeveling) {
-    expectedFlow.push({ name: 'Auto bed leveling - phase 1', stages: [1, 47] });
-  }
-  if (motorNoise) {
-    expectedFlow.push({ name: 'Motor noise cancellation', stages: [25] });
-  }
-  if (vibration) {
-    expectedFlow.push({ name: 'Vibration compensation', stages: [3] });
-  }
-  if (bedLeveling) {
-    expectedFlow.push({ name: 'Auto bed leveling - phase 2', stages: [48] });
-  }
-  if (isDualNozzle && nozzleOffset) {
-    expectedFlow.push({ name: 'Nozzle offset calibration', stages: [39] });
-  }
-  if (highTempHeatbed) {
-    expectedFlow.push({ name: 'High-temp heatbed calibration', stages: [40] });
-  }
-
-  // Find current step index
-  const currentStepIndex = expectedFlow.findIndex((step) =>
-    step.stages.includes(currentStage)
-  );
-
-  return (
-    <div
-      className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
-      onClick={onClose}
-    >
-      <Card className="w-full max-w-3xl max-h-[90vh] flex flex-col" onClick={(e: React.MouseEvent) => e.stopPropagation()}>
-        <CardContent className="p-0 flex flex-col h-full">
-          {/* Header */}
-          <div className="flex items-center justify-between px-4 py-3 border-b border-bambu-dark-tertiary">
-            <span className="text-sm font-medium text-white">Calibration</span>
-            <button
-              onClick={onClose}
-              className="p-1 rounded text-bambu-gray hover:bg-bambu-dark-tertiary hover:text-white"
-            >
-              <X className="w-4 h-4" />
-            </button>
-          </div>
-
-          {/* Content */}
-          <div className="flex-1 overflow-y-auto p-6">
-            {!isConnected && (
-              <div className="flex items-center gap-2 p-3 mb-4 bg-red-500/20 border border-red-500/50 rounded text-red-400">
-                <AlertTriangle className="w-4 h-4" />
-                <span className="text-sm">Printer not connected. Calibration cannot be started.</span>
-              </div>
-            )}
-
-            <div className="grid grid-cols-2 gap-8">
-              {/* Left column - Calibration step selection */}
-              <div>
-                <h3 className="text-base font-semibold text-white mb-4">Calibration step selection</h3>
-                <div className="space-y-4">
-                  {/* Bed leveling */}
-                  <div className="flex items-center gap-3">
-                    <Checkbox
-                      checked={bedLeveling}
-                      onChange={setBedLeveling}
-                      disabled={!isConnected || isCalibrating}
-                    />
-                    <span className="text-sm text-white">Bed leveling</span>
-                  </div>
-
-                  {/* Vibration compensation */}
-                  <div className="flex items-center gap-3">
-                    <Checkbox
-                      checked={vibration}
-                      onChange={setVibration}
-                      disabled={!isConnected || isCalibrating}
-                    />
-                    <span className="text-sm text-white">Vibration compensation</span>
-                  </div>
-
-                  {/* Motor noise cancellation */}
-                  <div className="flex items-center gap-3">
-                    <Checkbox
-                      checked={motorNoise}
-                      onChange={setMotorNoise}
-                      disabled={!isConnected || isCalibrating}
-                    />
-                    <span className="text-sm text-white">Motor noise cancellation</span>
-                  </div>
-
-                  {/* Nozzle offset calibration - only for dual nozzle printers */}
-                  {isDualNozzle && (
-                    <div className="flex items-center gap-3">
-                      <Checkbox
-                        checked={nozzleOffset}
-                        onChange={setNozzleOffset}
-                        disabled={!isConnected || isCalibrating}
-                      />
-                      <span className="text-sm text-white">Nozzle offset calibration</span>
-                    </div>
-                  )}
-
-                  {/* High-temperature Heatbed Calibration */}
-                  <div className="flex items-center gap-3">
-                    <Checkbox
-                      checked={highTempHeatbed}
-                      onChange={setHighTempHeatbed}
-                      disabled={!isConnected || isCalibrating}
-                    />
-                    <span className="text-sm text-white whitespace-nowrap">High-temperature Heatbed Calibration</span>
-                  </div>
-                </div>
-
-                {/* Calibration program description */}
-                <div className="mt-6">
-                  <h4 className="text-sm font-semibold text-white mb-2">Calibration program</h4>
-                  <p className="text-xs text-bambu-gray">
-                    The calibration program detects the status of your device automatically to minimize deviation.
-                    It keeps the device performing optimally.
-                  </p>
-                </div>
-              </div>
-
-              {/* Right column - Calibration Flow & Start button */}
-              <div className="flex flex-col">
-                <h3 className="text-base font-semibold text-bambu-green mb-4 text-center border-b border-bambu-dark-tertiary pb-2">
-                  Calibration Flow
-                </h3>
-
-                {/* Timeline progress indicator */}
-                <div className="flex-1 py-4 pl-4">
-                  {hasSelection ? (
-                    <div className="space-y-0">
-                      {expectedFlow.map((step, index) => {
-                        const isActive = calibrationStarted && !calibrationCompleted && step.stages.includes(currentStage);
-                        const isComplete = calibrationCompleted || (calibrationStarted && currentStepIndex > index);
-                        return (
-                          <TimelineStep
-                            key={step.name}
-                            step={index + 1}
-                            name={step.name}
-                            isActive={isActive}
-                            isComplete={isComplete}
-                            isLast={index === expectedFlow.length - 1}
-                          />
-                        );
-                      })}
-                      {/* Show current stage name if it's not in expected flow */}
-                      {currentStage >= 0 && currentStepIndex === -1 && status?.stg_cur_name && (
-                        <div className="mt-4 text-xs text-bambu-gray">
-                          Current: {status.stg_cur_name}
-                        </div>
-                      )}
-                    </div>
-                  ) : (
-                    <div className="flex items-center justify-center h-full text-sm text-bambu-gray italic">
-                      Select calibration steps
-                    </div>
-                  )}
-                </div>
-
-                {/* Start/Calibrating/Completed button */}
-                {calibrationCompleted ? (
-                  <div className="space-y-2">
-                    <button
-                      disabled
-                      className="w-full py-2.5 px-4 rounded-lg font-medium text-sm flex items-center justify-center gap-2 bg-bambu-green text-white cursor-default"
-                    >
-                      <Check className="w-4 h-4" />
-                      Completed
-                    </button>
-                    <button
-                      onClick={resetCalibration}
-                      className="w-full py-2 px-4 rounded-lg font-medium text-sm text-bambu-gray hover:text-white hover:bg-bambu-dark-tertiary transition-colors"
-                    >
-                      New Calibration
-                    </button>
-                  </div>
-                ) : (
-                  <button
-                    onClick={() => calibrationMutation.mutate()}
-                    disabled={!canStart}
-                    className={`w-full py-2.5 px-4 rounded-lg font-medium text-sm flex items-center justify-center gap-2 transition-colors ${
-                      isCalibrating
-                        ? 'bg-bambu-gray/50 text-white cursor-not-allowed'
-                        : canStart
-                        ? 'bg-bambu-green hover:bg-bambu-green/90 text-white'
-                        : 'bg-bambu-dark-tertiary text-bambu-gray cursor-not-allowed'
-                    }`}
-                  >
-                    {isCalibrating ? (
-                      <>
-                        <Loader2 className="w-4 h-4 animate-spin" />
-                        Calibrating
-                      </>
-                    ) : calibrationMutation.isPending ? (
-                      <>
-                        <Loader2 className="w-4 h-4 animate-spin" />
-                        Starting...
-                      </>
-                    ) : (
-                      'Start Calibration'
-                    )}
-                  </button>
-                )}
-
-                {calibrationMutation.isError && (
-                  <div className="mt-2 text-xs text-red-400 text-center">
-                    {calibrationMutation.error?.message || 'Failed to start calibration'}
-                  </div>
-                )}
-              </div>
-            </div>
-          </div>
-        </CardContent>
-      </Card>
-    </div>
-  );
-}

+ 0 - 130
frontend/src/components/control/CameraFeed.tsx

@@ -1,130 +0,0 @@
-import { useState, useRef } from 'react';
-import { api } from '../../api/client';
-import { Camera, CameraOff, Maximize2, RefreshCw, Loader2 } from 'lucide-react';
-
-interface CameraFeedProps {
-  printerId: number;
-  isConnected: boolean;
-}
-
-export function CameraFeed({ printerId, isConnected }: CameraFeedProps) {
-  const [streamEnabled, setStreamEnabled] = useState(false);
-  const [isLoading, setIsLoading] = useState(false);
-  const [error, setError] = useState<string | null>(null);
-  const imgRef = useRef<HTMLImageElement>(null);
-
-  const streamUrl = api.getCameraStreamUrl(printerId, 10);
-
-  const handleToggleStream = () => {
-    if (streamEnabled) {
-      setStreamEnabled(false);
-      setError(null);
-    } else {
-      setIsLoading(true);
-      setError(null);
-      setStreamEnabled(true);
-    }
-  };
-
-  const handleImageLoad = () => {
-    setIsLoading(false);
-    setError(null);
-  };
-
-  const handleImageError = () => {
-    setIsLoading(false);
-    setError('Failed to load camera stream');
-  };
-
-  const handleFullscreen = () => {
-    if (imgRef.current) {
-      if (document.fullscreenElement) {
-        document.exitFullscreen();
-      } else {
-        imgRef.current.requestFullscreen();
-      }
-    }
-  };
-
-  const handleRefresh = () => {
-    setStreamEnabled(false);
-    setTimeout(() => {
-      setIsLoading(true);
-      setStreamEnabled(true);
-    }, 100);
-  };
-
-  return (
-    <div className="relative w-full h-full bg-black">
-      {!streamEnabled ? (
-        <div className="absolute inset-0 flex flex-col items-center justify-center text-bambu-gray">
-          <div className="bg-bambu-dark-secondary rounded-lg p-6 flex flex-col items-center">
-            <Camera className="w-8 h-8 mb-2" />
-            <span className="text-sm mb-3">
-              {isConnected ? 'Click Start to view camera' : 'Printer not connected'}
-            </span>
-            <button
-              onClick={handleToggleStream}
-              disabled={!isConnected}
-              className="px-4 py-1.5 rounded text-sm bg-bambu-green text-white hover:bg-bambu-green-dark disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              Start
-            </button>
-          </div>
-        </div>
-      ) : (
-        <>
-          {isLoading && (
-            <div className="absolute inset-0 flex items-center justify-center">
-              <Loader2 className="w-8 h-8 animate-spin text-bambu-green" />
-            </div>
-          )}
-          {error ? (
-            <div className="absolute inset-0 flex flex-col items-center justify-center text-red-400">
-              <CameraOff className="w-12 h-12 mb-2" />
-              <span className="text-sm">{error}</span>
-              <button
-                onClick={handleRefresh}
-                className="mt-2 text-xs text-bambu-green hover:underline"
-              >
-                Retry
-              </button>
-            </div>
-          ) : (
-            <img
-              ref={imgRef}
-              src={streamUrl}
-              alt="Camera stream"
-              className="w-full h-full object-contain"
-              onLoad={handleImageLoad}
-              onError={handleImageError}
-            />
-          )}
-          {/* Overlay controls */}
-          <div className="absolute top-2 right-2 flex gap-2">
-            <button
-              onClick={handleRefresh}
-              className="p-2 rounded bg-gray-500 hover:bg-gray-400 text-white transition-colors shadow-lg"
-              title="Refresh stream"
-            >
-              <RefreshCw className="w-4 h-4" />
-            </button>
-            <button
-              onClick={handleFullscreen}
-              className="p-2 rounded bg-gray-500 hover:bg-gray-400 text-white transition-colors shadow-lg"
-              title="Fullscreen"
-            >
-              <Maximize2 className="w-4 h-4" />
-            </button>
-            <button
-              onClick={handleToggleStream}
-              className="px-3 py-1.5 rounded bg-red-600 hover:bg-red-500 text-white text-sm transition-colors shadow-lg"
-            >
-              Stop
-            </button>
-          </div>
-        </>
-      )}
-    </div>
-  );
-}

+ 0 - 167
frontend/src/components/control/ExtruderControls.tsx

@@ -1,167 +0,0 @@
-import { useState } from 'react';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { api, isConfirmationRequired } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { ChevronUp, ChevronDown } from 'lucide-react';
-import { ConfirmModal } from '../ConfirmModal';
-
-interface ExtruderControlsProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-  nozzleCount: number;
-  disabled?: boolean;
-}
-
-export function ExtruderControls({ printerId, status, nozzleCount, disabled = false }: ExtruderControlsProps) {
-  const queryClient = useQueryClient();
-  const isConnected = (status?.connected ?? false) && !disabled;
-  const isPrinting = status?.state === 'RUNNING' || status?.state === 'PAUSE';
-  const isDualNozzle = nozzleCount > 1;
-
-  // Active extruder from live status: 0=right, 1=left
-  const activeExtruder = status?.active_extruder ?? 0;
-
-  const [confirmModal, setConfirmModal] = useState<{
-    token: string;
-    warning: string;
-    distance: number;
-  } | null>(null);
-
-  const selectExtruderMutation = useMutation({
-    mutationFn: (extruder: number) => {
-      console.log('selectExtruder called with:', extruder);
-      return api.selectExtruder(printerId, extruder);
-    },
-    onSuccess: (data) => {
-      console.log('selectExtruder success:', data);
-      // Invalidate printer statuses to refresh the active extruder display
-      // Add a small delay to allow the printer to process the switch and MQTT to update
-      setTimeout(() => {
-        queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-      }, 2000);
-    },
-    onError: (error) => {
-      console.error('selectExtruder error:', error);
-    },
-  });
-
-  const extrudeMutation = useMutation({
-    mutationFn: ({ distance, token }: { distance: number; token?: string }) => {
-      // G-code for extrusion: relative mode, extrude, back to absolute
-      // Uses currently active extruder
-      const gcode = `G91\nG1 E${distance} F300\nG90`;
-      return api.sendGcode(printerId, gcode, token);
-    },
-    onSuccess: (result, variables) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          token: result.token,
-          warning: result.warning,
-          distance: variables.distance,
-        });
-      }
-    },
-  });
-
-  const handleExtrude = (distance: number) => {
-    extrudeMutation.mutate({ distance });
-  };
-
-  const handleConfirm = () => {
-    if (confirmModal) {
-      extrudeMutation.mutate({ distance: confirmModal.distance, token: confirmModal.token });
-      setConfirmModal(null);
-    }
-  };
-
-  const isDisabled = !isConnected || isPrinting || extrudeMutation.isPending;
-  const isSwitching = selectExtruderMutation.isPending;
-
-  // Get extruder image based on active state
-  const getExtruderImage = () => {
-    if (!isDualNozzle) return "/icons/single-extruder1.png";
-    // activeExtruder: 0=right, 1=left
-    return activeExtruder === 1 ? "/icons/dual-extruder-left.png" : "/icons/dual-extruder-right.png";
-  };
-
-  return (
-    <>
-      <div className="flex flex-col items-center gap-1.5 justify-center">
-        {/* Left/Right Toggle - only for dual nozzle */}
-        {isDualNozzle && (
-          <div className={`flex rounded-md overflow-hidden border border-bambu-dark-tertiary mb-1 flex-shrink-0 ${isDisabled || isSwitching ? 'opacity-50' : ''}`}>
-            <button
-              onClick={() => selectExtruderMutation.mutate(1)}
-              disabled={isDisabled || isSwitching}
-              className={`px-3 py-1.5 text-sm border-r border-bambu-dark-tertiary transition-colors disabled:cursor-not-allowed ${
-                activeExtruder === 1
-                  ? 'bg-bambu-green text-white'
-                  : 'bg-bambu-dark-secondary text-bambu-gray hover:bg-bambu-dark-tertiary disabled:hover:bg-bambu-dark-secondary'
-              }`}
-            >
-              Left
-            </button>
-            <button
-              onClick={() => selectExtruderMutation.mutate(0)}
-              disabled={isDisabled || isSwitching}
-              className={`px-3 py-1.5 text-sm transition-colors disabled:cursor-not-allowed ${
-                activeExtruder === 0
-                  ? 'bg-bambu-green text-white'
-                  : 'bg-bambu-dark-secondary text-bambu-gray hover:bg-bambu-dark-tertiary disabled:hover:bg-bambu-dark-secondary'
-              }`}
-            >
-              Right
-            </button>
-          </div>
-        )}
-
-        {/* Extrude Up Button */}
-        <button
-          onClick={() => handleExtrude(5)}
-          disabled={isDisabled}
-          className="w-9 h-[30px] rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary flex items-center justify-center text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-          title="Extrude 5mm"
-        >
-          <ChevronUp className="w-4 h-4" />
-        </button>
-
-        {/* Extruder Image */}
-        <div className="h-[120px] flex items-center justify-center">
-          <img
-            src={getExtruderImage()}
-            alt={isDualNozzle ? `${activeExtruder === 1 ? 'Left' : 'Right'} Extruder Active` : "Single Extruder"}
-            className="h-full object-contain"
-            onError={(e) => {
-              (e.target as HTMLImageElement).style.display = 'none';
-            }}
-          />
-        </div>
-
-        {/* Retract Down Button */}
-        <button
-          onClick={() => handleExtrude(-5)}
-          disabled={isDisabled}
-          className="w-9 h-[30px] rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary flex items-center justify-center text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-          title="Retract 5mm"
-        >
-          <ChevronDown className="w-4 h-4" />
-        </button>
-
-        {/* Label */}
-        <span className="text-xs text-bambu-gray mt-0.5">Extruder</span>
-      </div>
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Extrusion"
-          message={confirmModal.warning}
-          confirmText="Continue"
-          variant="warning"
-          onConfirm={handleConfirm}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 135
frontend/src/components/control/FanControls.tsx

@@ -1,135 +0,0 @@
-import { useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { Fan, Loader2 } from 'lucide-react';
-
-interface FanControlsProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-interface FanSliderProps {
-  label: string;
-  value: number;
-  onChange: (value: number) => void;
-  disabled: boolean;
-  isLoading: boolean;
-}
-
-function FanSlider({ label, value, onChange, disabled, isLoading }: FanSliderProps) {
-  const [localValue, setLocalValue] = useState(value);
-  const [isDragging, setIsDragging] = useState(false);
-
-  const displayValue = isDragging ? localValue : value;
-
-  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-    const newValue = parseInt(e.target.value, 10);
-    setLocalValue(newValue);
-  };
-
-  const handleMouseUp = () => {
-    setIsDragging(false);
-    if (localValue !== value) {
-      onChange(localValue);
-    }
-  };
-
-  return (
-    <div className="p-3 rounded bg-bambu-dark">
-      <div className="flex items-center justify-between mb-2">
-        <span className="text-sm text-bambu-gray">{label}</span>
-        <div className="flex items-center gap-2">
-          <span className="text-sm font-mono text-white">{displayValue}%</span>
-          {isLoading && <Loader2 className="w-3 h-3 animate-spin" />}
-        </div>
-      </div>
-      <input
-        type="range"
-        min="0"
-        max="100"
-        step="5"
-        value={displayValue}
-        onChange={handleChange}
-        onMouseDown={() => setIsDragging(true)}
-        onMouseUp={handleMouseUp}
-        onTouchStart={() => setIsDragging(true)}
-        onTouchEnd={handleMouseUp}
-        disabled={disabled}
-        className="w-full h-2 bg-bambu-dark-tertiary rounded-lg appearance-none cursor-pointer disabled:cursor-not-allowed disabled:opacity-50
-          [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-bambu-green [&::-webkit-slider-thumb]:cursor-pointer
-          [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-bambu-green [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer"
-      />
-      <div className="flex justify-between text-xs text-bambu-gray mt-1">
-        <span>Off</span>
-        <span>Max</span>
-      </div>
-    </div>
-  );
-}
-
-export function FanControls({ printerId, status }: FanControlsProps) {
-  const isConnected = status?.connected ?? false;
-
-  // Note: Bambu printers don't report fan speeds via MQTT
-  // So we track locally and can't show actual current values
-  const [fanValues, setFanValues] = useState({
-    part: 100,
-    aux: 0,
-    chamber: 0,
-  });
-
-  const partFanMutation = useMutation({
-    mutationFn: (speed: number) => api.setPartFan(printerId, speed),
-    onSuccess: (_, speed) => setFanValues((prev) => ({ ...prev, part: speed })),
-  });
-
-  const auxFanMutation = useMutation({
-    mutationFn: (speed: number) => api.setAuxFan(printerId, speed),
-    onSuccess: (_, speed) => setFanValues((prev) => ({ ...prev, aux: speed })),
-  });
-
-  const chamberFanMutation = useMutation({
-    mutationFn: (speed: number) => api.setChamberFan(printerId, speed),
-    onSuccess: (_, speed) => setFanValues((prev) => ({ ...prev, chamber: speed })),
-  });
-
-  return (
-    <div className="bg-bambu-dark-secondary rounded-lg p-4">
-      <div className="flex items-center gap-2 mb-4">
-        <Fan className="w-4 h-4 text-bambu-gray" />
-        <h3 className="text-sm font-medium">Fans</h3>
-      </div>
-
-      <div className="space-y-3">
-        <FanSlider
-          label="Part Cooling"
-          value={fanValues.part}
-          onChange={(v) => partFanMutation.mutate(v)}
-          disabled={!isConnected}
-          isLoading={partFanMutation.isPending}
-        />
-        <FanSlider
-          label="Auxiliary"
-          value={fanValues.aux}
-          onChange={(v) => auxFanMutation.mutate(v)}
-          disabled={!isConnected}
-          isLoading={auxFanMutation.isPending}
-        />
-        <FanSlider
-          label="Chamber"
-          value={fanValues.chamber}
-          onChange={(v) => chamberFanMutation.mutate(v)}
-          disabled={!isConnected}
-          isLoading={chamberFanMutation.isPending}
-        />
-      </div>
-
-      {(partFanMutation.error || auxFanMutation.error || chamberFanMutation.error) && (
-        <p className="mt-2 text-sm text-red-400">
-          {(partFanMutation.error || auxFanMutation.error || chamberFanMutation.error)?.message}
-        </p>
-      )}
-    </div>
-  );
-}

+ 0 - 230
frontend/src/components/control/JogPad.tsx

@@ -1,230 +0,0 @@
-import { useMutation } from '@tanstack/react-query';
-import { api, isConfirmationRequired } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { useState, useId } from 'react';
-import { ConfirmModal } from '../ConfirmModal';
-
-interface JogPadProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-  disabled?: boolean;
-}
-
-// Image map coordinates for 220x220 jog pad
-// The jog pad has concentric rings: outer (10mm), inner (1mm), center (home)
-const SIZE = 220;
-const CENTER = SIZE / 2; // 110
-
-// Ring radii (approximate based on typical jog pad design)
-const OUTER_RADIUS = 108;  // Outer edge
-const OUTER_INNER = 72;    // Inner edge of outer ring (10mm zone)
-const INNER_INNER = 35;    // Inner edge of inner ring (1mm zone)
-const HOME_RADIUS = 28;    // Home button radius
-
-// Generate polygon points for a ring segment (pie slice)
-function ringSegment(
-  cx: number, cy: number,
-  innerR: number, outerR: number,
-  startAngle: number, endAngle: number,
-  steps: number = 8
-): string {
-  const points: string[] = [];
-
-  // Outer arc (clockwise)
-  for (let i = 0; i <= steps; i++) {
-    const angle = startAngle + (endAngle - startAngle) * (i / steps);
-    const x = Math.round(cx + outerR * Math.cos(angle));
-    const y = Math.round(cy + outerR * Math.sin(angle));
-    points.push(`${x},${y}`);
-  }
-
-  // Inner arc (counter-clockwise)
-  for (let i = steps; i >= 0; i--) {
-    const angle = startAngle + (endAngle - startAngle) * (i / steps);
-    const x = Math.round(cx + innerR * Math.cos(angle));
-    const y = Math.round(cy + innerR * Math.sin(angle));
-    points.push(`${x},${y}`);
-  }
-
-  return points.join(',');
-}
-
-// Angle definitions (in radians, 0 = right, going clockwise)
-// Each direction covers 90 degrees (π/2), offset by 45 degrees (π/4)
-const ANGLES = {
-  up:    { start: -Math.PI * 3/4, end: -Math.PI / 4 },     // Top: -135° to -45°
-  right: { start: -Math.PI / 4, end: Math.PI / 4 },         // Right: -45° to 45°
-  down:  { start: Math.PI / 4, end: Math.PI * 3/4 },        // Bottom: 45° to 135°
-  left:  { start: Math.PI * 3/4, end: Math.PI * 5/4 },      // Left: 135° to 225° (or -135°)
-};
-
-export function JogPad({ printerId, status, disabled = false }: JogPadProps) {
-  const isConnected = (status?.connected ?? false) && !disabled;
-  const mapId = useId();
-
-  const [confirmModal, setConfirmModal] = useState<{
-    action: string;
-    token: string;
-    warning: string;
-    onConfirm: () => void;
-  } | null>(null);
-
-  const homeMutation = useMutation({
-    mutationFn: ({ axes, token }: { axes: string; token?: string }) =>
-      api.homeAxes(printerId, axes, token),
-    onSuccess: (result) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'home',
-          token: result.token,
-          warning: result.warning,
-          onConfirm: () => homeMutation.mutate({ axes: 'XY', token: result.token }),
-        });
-      }
-    },
-  });
-
-  const moveMutation = useMutation({
-    mutationFn: ({ axis, distance, token }: { axis: string; distance: number; token?: string }) =>
-      api.moveAxis(printerId, axis, distance, 3000, token),
-    onSuccess: (result, variables) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'move',
-          token: result.token,
-          warning: result.warning,
-          onConfirm: () =>
-            moveMutation.mutate({
-              axis: variables.axis,
-              distance: variables.distance,
-              token: result.token,
-            }),
-        });
-      }
-    },
-  });
-
-  const handleHome = () => {
-    if (isDisabled) return;
-    homeMutation.mutate({ axes: 'XY' });
-  };
-
-  const handleMove = (axis: string, distance: number) => {
-    if (isDisabled) return;
-    moveMutation.mutate({ axis, distance });
-  };
-
-  const handleConfirm = () => {
-    if (confirmModal) {
-      confirmModal.onConfirm();
-      setConfirmModal(null);
-    }
-  };
-
-  const isLoading = homeMutation.isPending || moveMutation.isPending;
-  const isDisabled = !isConnected || isLoading;
-
-  // Generate coordinates for circle (home button)
-  const homeCoords = Array.from({ length: 16 }, (_, i) => {
-    const angle = (i / 16) * Math.PI * 2;
-    const x = Math.round(CENTER + HOME_RADIUS * Math.cos(angle));
-    const y = Math.round(CENTER + HOME_RADIUS * Math.sin(angle));
-    return `${x},${y}`;
-  }).join(',');
-
-  return (
-    <>
-      <div className="relative w-[220px] h-[220px] mb-3.5">
-        <img
-          src="/icons/jogpad.svg"
-          alt="Jog Pad"
-          useMap={`#${mapId}`}
-          className="w-full h-full jogpad-theme"
-        />
-
-        <map name={mapId}>
-          {/* Outer ring - 10mm moves */}
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, OUTER_INNER, OUTER_RADIUS, ANGLES.up.start, ANGLES.up.end)}
-            onClick={() => handleMove('Y', 10)}
-            title="Y+10mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, OUTER_INNER, OUTER_RADIUS, ANGLES.down.start, ANGLES.down.end)}
-            onClick={() => handleMove('Y', -10)}
-            title="Y-10mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, OUTER_INNER, OUTER_RADIUS, ANGLES.left.start, ANGLES.left.end)}
-            onClick={() => handleMove('X', -10)}
-            title="X-10mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, OUTER_INNER, OUTER_RADIUS, ANGLES.right.start, ANGLES.right.end)}
-            onClick={() => handleMove('X', 10)}
-            title="X+10mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-
-          {/* Inner ring - 1mm moves */}
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, INNER_INNER, OUTER_INNER, ANGLES.up.start, ANGLES.up.end)}
-            onClick={() => handleMove('Y', 1)}
-            title="Y+1mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, INNER_INNER, OUTER_INNER, ANGLES.down.start, ANGLES.down.end)}
-            onClick={() => handleMove('Y', -1)}
-            title="Y-1mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, INNER_INNER, OUTER_INNER, ANGLES.left.start, ANGLES.left.end)}
-            onClick={() => handleMove('X', -1)}
-            title="X-1mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-          <area
-            shape="poly"
-            coords={ringSegment(CENTER, CENTER, INNER_INNER, OUTER_INNER, ANGLES.right.start, ANGLES.right.end)}
-            onClick={() => handleMove('X', 1)}
-            title="X+1mm"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-
-          {/* Center - Home button */}
-          <area
-            shape="poly"
-            coords={homeCoords}
-            onClick={handleHome}
-            title="Home XY"
-            style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}
-          />
-        </map>
-      </div>
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Action"
-          message={confirmModal.warning}
-          confirmText="Continue"
-          variant="warning"
-          onConfirm={handleConfirm}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 68
frontend/src/components/control/LightToggle.tsx

@@ -1,68 +0,0 @@
-import { useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { Lightbulb, LightbulbOff, Loader2 } from 'lucide-react';
-
-interface LightToggleProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-export function LightToggle({ printerId, status }: LightToggleProps) {
-  const isConnected = status?.connected ?? false;
-
-  // Note: Bambu printers don't report light state via standard MQTT
-  // Track locally
-  const [isOn, setIsOn] = useState(true);
-
-  const lightMutation = useMutation({
-    mutationFn: (on: boolean) => api.setChamberLight(printerId, on),
-    onSuccess: (_, on) => setIsOn(on),
-  });
-
-  const handleToggle = () => {
-    lightMutation.mutate(!isOn);
-  };
-
-  return (
-    <div className="bg-bambu-dark-secondary rounded-lg p-4">
-      <div className="flex items-center justify-between">
-        <div className="flex items-center gap-2">
-          {isOn ? (
-            <Lightbulb className="w-4 h-4 text-yellow-400" />
-          ) : (
-            <LightbulbOff className="w-4 h-4 text-bambu-gray" />
-          )}
-          <h3 className="text-sm font-medium">Chamber Light</h3>
-        </div>
-
-        <button
-          onClick={handleToggle}
-          disabled={!isConnected || lightMutation.isPending}
-          className={`relative w-12 h-6 rounded-full transition-colors ${
-            isOn ? 'bg-bambu-green' : 'bg-bambu-dark-tertiary'
-          } disabled:opacity-50 disabled:cursor-not-allowed`}
-        >
-          {lightMutation.isPending ? (
-            <div className="absolute inset-0 flex items-center justify-center">
-              <Loader2 className="w-4 h-4 animate-spin" />
-            </div>
-          ) : (
-            <div
-              className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-transform ${
-                isOn ? 'translate-x-7' : 'translate-x-1'
-              }`}
-            />
-          )}
-        </button>
-      </div>
-
-      {lightMutation.error && (
-        <p className="mt-2 text-sm text-red-400">
-          {lightMutation.error.message}
-        </p>
-      )}
-    </div>
-  );
-}

+ 0 - 235
frontend/src/components/control/MovementControls.tsx

@@ -1,235 +0,0 @@
-import { useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api, isConfirmationRequired } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { Home, Move, Loader2, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Power } from 'lucide-react';
-import { ConfirmModal } from '../ConfirmModal';
-
-interface MovementControlsProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-export function MovementControls({ printerId, status }: MovementControlsProps) {
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING' || status?.state === 'PAUSE';
-
-  const [confirmModal, setConfirmModal] = useState<{
-    action: string;
-    token: string;
-    warning: string;
-    onConfirm: () => void;
-  } | null>(null);
-
-  const [moveDistance, setMoveDistance] = useState(10);
-
-  const homeMutation = useMutation({
-    mutationFn: ({ axes, token }: { axes: string; token?: string }) =>
-      api.homeAxes(printerId, axes, token),
-    onSuccess: (result) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'home',
-          token: result.token,
-          warning: result.warning,
-          onConfirm: () => homeMutation.mutate({ axes: 'XYZ', token: result.token }),
-        });
-      }
-    },
-  });
-
-  const moveMutation = useMutation({
-    mutationFn: ({ axis, distance, token }: { axis: string; distance: number; token?: string }) =>
-      api.moveAxis(printerId, axis, distance, 3000, token),
-    onSuccess: (result, variables) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'move',
-          token: result.token,
-          warning: result.warning,
-          onConfirm: () =>
-            moveMutation.mutate({
-              axis: variables.axis,
-              distance: variables.distance,
-              token: result.token,
-            }),
-        });
-      }
-    },
-  });
-
-  const disableMotorsMutation = useMutation({
-    mutationFn: (token?: string) => api.disableMotors(printerId, token),
-    onSuccess: (result) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'disable',
-          token: result.token,
-          warning: result.warning,
-          onConfirm: () => disableMotorsMutation.mutate(result.token),
-        });
-      }
-    },
-  });
-
-  const handleHome = () => {
-    homeMutation.mutate({ axes: 'XYZ' });
-  };
-
-  const handleMove = (axis: string, distance: number) => {
-    moveMutation.mutate({ axis, distance });
-  };
-
-  const handleDisableMotors = () => {
-    disableMotorsMutation.mutate(undefined);
-  };
-
-  const handleConfirm = () => {
-    if (confirmModal) {
-      confirmModal.onConfirm();
-      setConfirmModal(null);
-    }
-  };
-
-  const isLoading =
-    homeMutation.isPending || moveMutation.isPending || disableMotorsMutation.isPending;
-
-  return (
-    <>
-      <div className="bg-bambu-dark-secondary rounded-lg p-4">
-        <div className="flex items-center justify-between mb-4">
-          <div className="flex items-center gap-2">
-            <Move className="w-4 h-4 text-bambu-gray" />
-            <h3 className="text-sm font-medium">Movement</h3>
-          </div>
-          {isLoading && <Loader2 className="w-4 h-4 animate-spin text-bambu-green" />}
-        </div>
-
-        {isPrinting && (
-          <div className="mb-4 p-2 rounded bg-yellow-500/20 text-yellow-500 text-xs text-center">
-            Movement is restricted during printing
-          </div>
-        )}
-
-        {/* Distance Selector */}
-        <div className="flex items-center justify-center gap-2 mb-4">
-          <span className="text-xs text-bambu-gray">Distance:</span>
-          {[1, 10, 50].map((d) => (
-            <button
-              key={d}
-              onClick={() => setMoveDistance(d)}
-              className={`px-3 py-1 rounded text-xs transition-colors ${
-                moveDistance === d
-                  ? 'bg-bambu-green text-white'
-                  : 'bg-bambu-dark-tertiary text-bambu-gray hover:bg-bambu-dark hover:text-white'
-              }`}
-            >
-              {d}mm
-            </button>
-          ))}
-        </div>
-
-        {/* Movement Grid */}
-        <div className="grid grid-cols-5 gap-2 mb-4">
-          {/* Row 1: Y+ */}
-          <div />
-          <div />
-          <button
-            onClick={() => handleMove('Y', moveDistance)}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            title={`Y+${moveDistance}`}
-          >
-            <ChevronUp className="w-5 h-5 mx-auto" />
-          </button>
-          <div />
-          <button
-            onClick={() => handleMove('Z', moveDistance)}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            title={`Z+${moveDistance}`}
-          >
-            <ChevronUp className="w-5 h-5 mx-auto text-blue-400" />
-          </button>
-
-          {/* Row 2: X-, Home, X+ */}
-          <div />
-          <button
-            onClick={() => handleMove('X', -moveDistance)}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            title={`X-${moveDistance}`}
-          >
-            <ChevronLeft className="w-5 h-5 mx-auto" />
-          </button>
-          <button
-            onClick={handleHome}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-green/20 text-bambu-green hover:bg-bambu-green/30 disabled:opacity-50 disabled:cursor-not-allowed"
-            title="Home all axes"
-          >
-            <Home className="w-5 h-5 mx-auto" />
-          </button>
-          <button
-            onClick={() => handleMove('X', moveDistance)}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            title={`X+${moveDistance}`}
-          >
-            <ChevronRight className="w-5 h-5 mx-auto" />
-          </button>
-          <div className="text-center text-xs text-bambu-gray self-center">Z</div>
-
-          {/* Row 3: Y- */}
-          <div />
-          <div />
-          <button
-            onClick={() => handleMove('Y', -moveDistance)}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            title={`Y-${moveDistance}`}
-          >
-            <ChevronDown className="w-5 h-5 mx-auto" />
-          </button>
-          <div />
-          <button
-            onClick={() => handleMove('Z', -moveDistance)}
-            disabled={!isConnected || isLoading}
-            className="p-3 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            title={`Z-${moveDistance}`}
-          >
-            <ChevronDown className="w-5 h-5 mx-auto text-blue-400" />
-          </button>
-        </div>
-
-        {/* Disable Motors */}
-        <button
-          onClick={handleDisableMotors}
-          disabled={!isConnected || isLoading}
-          className="w-full flex items-center justify-center gap-2 px-4 py-2 rounded bg-bambu-dark hover:bg-bambu-dark-tertiary text-bambu-gray hover:text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-        >
-          <Power className="w-4 h-4" />
-          <span className="text-sm">Disable Motors</span>
-        </button>
-
-        {(homeMutation.error || moveMutation.error || disableMotorsMutation.error) && (
-          <p className="mt-2 text-sm text-red-400">
-            {(homeMutation.error || moveMutation.error || disableMotorsMutation.error)?.message}
-          </p>
-        )}
-      </div>
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Action"
-          message={confirmModal.warning}
-          confirmText="Continue"
-          variant="warning"
-          onConfirm={handleConfirm}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 137
frontend/src/components/control/PrintControls.tsx

@@ -1,137 +0,0 @@
-import { useState } from 'react';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { api, isConfirmationRequired } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { Pause, Play, Square, Loader2 } from 'lucide-react';
-import { ConfirmModal } from '../ConfirmModal';
-
-interface PrintControlsProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-export function PrintControls({ printerId, status }: PrintControlsProps) {
-  const queryClient = useQueryClient();
-  const [confirmModal, setConfirmModal] = useState<{
-    action: string;
-    token: string;
-    warning: string;
-  } | null>(null);
-
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING';
-  const isPaused = status?.state === 'PAUSE';
-
-  const pauseMutation = useMutation({
-    mutationFn: () => api.pausePrint(printerId),
-    onSuccess: () => {
-      queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-    },
-  });
-
-  const resumeMutation = useMutation({
-    mutationFn: () => api.resumePrint(printerId),
-    onSuccess: () => {
-      queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-    },
-  });
-
-  const stopMutation = useMutation({
-    mutationFn: (token?: string) => api.stopPrint(printerId, token),
-    onSuccess: (result) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'stop',
-          token: result.token,
-          warning: result.warning,
-        });
-      } else {
-        queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-      }
-    },
-  });
-
-  const handleStop = () => {
-    stopMutation.mutate(undefined);
-  };
-
-  const handleConfirmStop = () => {
-    if (confirmModal) {
-      stopMutation.mutate(confirmModal.token);
-      setConfirmModal(null);
-    }
-  };
-
-  const isLoading = pauseMutation.isPending || resumeMutation.isPending || stopMutation.isPending;
-
-  return (
-    <>
-      <div>
-        <h3 className="text-sm font-medium text-bambu-gray mb-3">Print Controls</h3>
-
-        <div className="flex gap-2">
-          {/* Pause Button */}
-          <button
-            onClick={() => pauseMutation.mutate()}
-            disabled={!isConnected || !isPrinting || isLoading}
-            className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-yellow-500/20 text-yellow-500 hover:bg-yellow-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-          >
-            {pauseMutation.isPending ? (
-              <Loader2 className="w-5 h-5 animate-spin" />
-            ) : (
-              <Pause className="w-5 h-5" />
-            )}
-            <span className="font-medium">Pause</span>
-          </button>
-
-          {/* Resume Button */}
-          <button
-            onClick={() => resumeMutation.mutate()}
-            disabled={!isConnected || !isPaused || isLoading}
-            className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-bambu-green/20 text-bambu-green hover:bg-bambu-green/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-          >
-            {resumeMutation.isPending ? (
-              <Loader2 className="w-5 h-5 animate-spin" />
-            ) : (
-              <Play className="w-5 h-5" />
-            )}
-            <span className="font-medium">Resume</span>
-          </button>
-
-          {/* Stop Button */}
-          <button
-            onClick={handleStop}
-            disabled={!isConnected || (!isPrinting && !isPaused) || isLoading}
-            className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-red-500/20 text-red-500 hover:bg-red-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-          >
-            {stopMutation.isPending ? (
-              <Loader2 className="w-5 h-5 animate-spin" />
-            ) : (
-              <Square className="w-5 h-5" />
-            )}
-            <span className="font-medium">Stop</span>
-          </button>
-        </div>
-
-        {/* Error Message */}
-        {(pauseMutation.error || resumeMutation.error || stopMutation.error) && (
-          <p className="mt-2 text-sm text-red-400">
-            {(pauseMutation.error || resumeMutation.error || stopMutation.error)?.message}
-          </p>
-        )}
-      </div>
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Stop"
-          message={confirmModal.warning}
-          confirmText="Stop Print"
-          variant="danger"
-          onConfirm={handleConfirmStop}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 393
frontend/src/components/control/PrintOptionsModal.tsx

@@ -1,393 +0,0 @@
-import { useEffect, useState } from 'react';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { Printer, PrinterStatus } from '../../api/client';
-import { X, Loader2, AlertTriangle, ChevronDown } from 'lucide-react';
-import { Card, CardContent } from '../Card';
-
-interface PrintOptionsModalProps {
-  printer: Printer;
-  status: PrinterStatus | null | undefined;
-  onClose: () => void;
-}
-
-// Checkbox component matching Bambu Studio style
-function Checkbox({
-  checked,
-  onChange,
-  disabled,
-  loading,
-}: {
-  checked: boolean;
-  onChange: (checked: boolean) => void;
-  disabled?: boolean;
-  loading?: boolean;
-}) {
-  return (
-    <button
-      onClick={() => !disabled && !loading && onChange(!checked)}
-      disabled={disabled || loading}
-      className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-colors ${
-        checked
-          ? 'bg-bambu-green border-bambu-green'
-          : 'bg-transparent border-bambu-gray'
-      } ${disabled || loading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
-    >
-      {loading ? (
-        <Loader2 className="w-3 h-3 animate-spin text-white" />
-      ) : checked ? (
-        <svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
-          <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
-        </svg>
-      ) : null}
-    </button>
-  );
-}
-
-// Sensitivity dropdown component
-function SensitivityDropdown({
-  value,
-  onChange,
-  disabled,
-  loading,
-}: {
-  value: string;
-  onChange: (value: string) => void;
-  disabled?: boolean;
-  loading?: boolean;
-}) {
-  const [isOpen, setIsOpen] = useState(false);
-  const options = ['Low', 'Medium', 'High'];
-
-  const displayValue = value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
-  const isDisabled = disabled || loading;
-
-  return (
-    <div className="relative">
-      <button
-        onClick={() => !isDisabled && setIsOpen(!isOpen)}
-        disabled={isDisabled}
-        className={`flex items-center gap-2 px-3 py-1.5 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded text-sm text-white min-w-[100px] justify-between ${
-          isDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:border-bambu-gray'
-        }`}
-      >
-        {loading ? (
-          <Loader2 className="w-4 h-4 animate-spin text-bambu-green" />
-        ) : (
-          <span>{displayValue}</span>
-        )}
-        <ChevronDown className="w-4 h-4 text-bambu-gray" />
-      </button>
-      {isOpen && !isDisabled && (
-        <>
-          <div className="fixed inset-0 z-10" onClick={() => setIsOpen(false)} />
-          <div className="absolute top-full left-0 mt-1 w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded shadow-lg z-20">
-            {options.map((option) => (
-              <button
-                key={option}
-                onClick={() => {
-                  onChange(option.toLowerCase());
-                  setIsOpen(false);
-                }}
-                className={`w-full px-3 py-2 text-left text-sm hover:bg-bambu-dark-tertiary ${
-                  value.toLowerCase() === option.toLowerCase() ? 'text-bambu-green' : 'text-white'
-                }`}
-              >
-                {option}
-              </button>
-            ))}
-          </div>
-        </>
-      )}
-    </div>
-  );
-}
-
-export function PrintOptionsModal({ printer, status, onClose }: PrintOptionsModalProps) {
-  const queryClient = useQueryClient();
-  const printOptions = status?.print_options;
-  const isConnected = status?.connected ?? false;
-
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') onClose();
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose]);
-
-  // Set print option mutation - track both UI and API module names
-  const setOptionMutation = useMutation({
-    mutationFn: ({
-      apiModuleName,
-      enabled,
-      printHalt,
-      sensitivity,
-    }: {
-      uiModuleName: string;  // The module name from the UI (for loading state) - accessed via variables
-      apiModuleName: string; // The module name sent to API (may differ due to firmware quirk)
-      enabled: boolean;
-      printHalt?: boolean;
-      sensitivity?: string;
-    }) => api.setPrintOption(printer.id, apiModuleName, enabled, printHalt, sensitivity),
-    onSuccess: () => {
-      queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-    },
-  });
-
-  const handleToggle = (moduleName: string, enabled: boolean, sensitivity?: string) => {
-    const printHalt = enabled && sensitivity !== undefined && sensitivity !== 'never_halt';
-    setOptionMutation.mutate({
-      uiModuleName: moduleName,
-      apiModuleName: moduleName,
-      enabled,
-      printHalt,
-      sensitivity: sensitivity ?? 'medium'
-    });
-  };
-
-  const handleSensitivityChange = (moduleName: string, sensitivity: string) => {
-    // Spaghetti and Pileup sensitivities are linked in firmware
-    // For pileup, we send spaghetti_detector to set the shared sensitivity
-    // All other detectors use their own module name
-    let apiModuleName = moduleName;
-    if (moduleName === 'pileup_detector') {
-      // Pileup sensitivity is linked to spaghetti - use spaghetti_detector
-      apiModuleName = 'spaghetti_detector';
-    }
-
-    setOptionMutation.mutate({
-      uiModuleName: moduleName,
-      apiModuleName,
-      enabled: true,
-      printHalt: true,
-      sensitivity
-    });
-  };
-
-  // Check loading state using the UI module name (not API module name)
-  const isToggling = (moduleName: string) =>
-    setOptionMutation.isPending && setOptionMutation.variables?.uiModuleName === moduleName;
-
-  // Check if sensitivity is being changed for a specific detector
-  // This also returns true for linked detectors (e.g., spaghetti loading shows for pileup too)
-  const isSensitivityLoading = (moduleName: string) => {
-    if (!setOptionMutation.isPending || !setOptionMutation.variables?.sensitivity) return false;
-    const uiModule = setOptionMutation.variables.uiModuleName;
-    // Direct match
-    if (uiModule === moduleName) return true;
-    // Linked: spaghetti and pileup share sensitivity
-    if ((uiModule === 'spaghetti_detector' && moduleName === 'pileup_detector') ||
-        (uiModule === 'pileup_detector' && moduleName === 'spaghetti_detector')) return true;
-    return false;
-  };
-
-  return (
-    <div
-      className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
-      onClick={onClose}
-    >
-      <Card className="w-full max-w-xl max-h-[90vh] flex flex-col" onClick={(e: React.MouseEvent) => e.stopPropagation()}>
-        <CardContent className="p-0 flex flex-col h-full">
-          {/* Header */}
-          <div className="flex items-center justify-between px-4 py-3 border-b border-bambu-dark-tertiary">
-            <span className="text-sm font-medium text-white">Print Options</span>
-            <button
-              onClick={onClose}
-              className="p-1 rounded hover:bg-bambu-dark-tertiary text-bambu-gray hover:text-white"
-            >
-              <X className="w-4 h-4" />
-            </button>
-          </div>
-
-          {/* Content - Scrollable */}
-          <div className="flex-1 overflow-y-auto p-6 space-y-6">
-            {!isConnected && (
-              <div className="flex items-center gap-2 p-3 bg-red-500/20 border border-red-500/50 rounded text-red-400">
-                <AlertTriangle className="w-4 h-4" />
-                <span className="text-sm">Printer not connected. Options cannot be changed.</span>
-              </div>
-            )}
-
-            {!printOptions ? (
-              <div className="flex items-center gap-2 text-bambu-gray">
-                <AlertTriangle className="w-4 h-4" />
-                <span className="text-sm">Print options not available. Try refreshing.</span>
-              </div>
-            ) : (
-              <>
-                {/* AI Detections Section */}
-                <div>
-                  <h3 className="text-base font-semibold text-white mb-1">AI Detections</h3>
-                  <p className="text-xs text-bambu-gray mb-4">
-                    Printer will send assistant message or pause printing if any of the following problem is detected.
-                  </p>
-                  <div className="space-y-5">
-                    {/* Spaghetti Detection */}
-                    <div className="space-y-2">
-                      <div className="flex items-center gap-3">
-                        <Checkbox
-                          checked={printOptions.spaghetti_detector}
-                          onChange={(checked) => handleToggle('spaghetti_detector', checked, printOptions.halt_print_sensitivity)}
-                          disabled={!isConnected}
-                          loading={isToggling('spaghetti_detector')}
-                        />
-                        <span className="text-sm font-medium text-white">Spaghetti Detection</span>
-                      </div>
-                      <p className="text-xs text-bambu-gray ml-8">
-                        Detects spaghetti failure (scattered loose filament).
-                      </p>
-                      <div className="flex items-center gap-3 ml-8">
-                        <span className="text-xs text-bambu-gray">Pausing Sensitivity:</span>
-                        <SensitivityDropdown
-                          value={printOptions.halt_print_sensitivity || 'medium'}
-                          onChange={(value) => handleSensitivityChange('spaghetti_detector', value)}
-                          disabled={!isConnected || !printOptions.spaghetti_detector}
-                          loading={isSensitivityLoading('spaghetti_detector')}
-                        />
-                      </div>
-                    </div>
-
-                    {/* Purge Chute Pile-Up Detection */}
-                    <div className="space-y-2">
-                      <div className="flex items-center gap-3">
-                        <Checkbox
-                          checked={printOptions.pileup_detector}
-                          onChange={(checked) => handleToggle('pileup_detector', checked, printOptions.pileup_sensitivity)}
-                          disabled={!isConnected}
-                          loading={isToggling('pileup_detector')}
-                        />
-                        <span className="text-sm font-medium text-white">Purge Chute Pile-Up Detection</span>
-                      </div>
-                      <p className="text-xs text-bambu-gray ml-8">
-                        Monitors if the waste is piled up in the purge chute.
-                      </p>
-                      <div className="flex items-center gap-3 ml-8">
-                        <span className="text-xs text-bambu-gray">Pausing Sensitivity:</span>
-                        <SensitivityDropdown
-                          value={printOptions.pileup_sensitivity || 'medium'}
-                          onChange={(value) => handleSensitivityChange('pileup_detector', value)}
-                          disabled={!isConnected || !printOptions.pileup_detector}
-                          loading={isSensitivityLoading('pileup_detector')}
-                        />
-                      </div>
-                    </div>
-
-                    {/* Nozzle Clumping Detection */}
-                    <div className="space-y-2">
-                      <div className="flex items-center gap-3">
-                        <Checkbox
-                          checked={printOptions.nozzle_clumping_detector}
-                          onChange={(checked) => handleToggle('clump_detector', checked, printOptions.nozzle_clumping_sensitivity)}
-                          disabled={!isConnected}
-                          loading={isToggling('clump_detector')}
-                        />
-                        <span className="text-sm font-medium text-white">Nozzle Clumping Detection</span>
-                      </div>
-                      <p className="text-xs text-bambu-gray ml-8">
-                        Checks if the nozzle is clumping by filaments or other foreign objects.
-                      </p>
-                      <div className="flex items-center gap-3 ml-8">
-                        <span className="text-xs text-bambu-gray">Pausing Sensitivity:</span>
-                        <SensitivityDropdown
-                          value={printOptions.nozzle_clumping_sensitivity || 'medium'}
-                          onChange={(value) => handleSensitivityChange('clump_detector', value)}
-                          disabled={!isConnected || !printOptions.nozzle_clumping_detector}
-                          loading={isSensitivityLoading('clump_detector')}
-                        />
-                      </div>
-                    </div>
-
-                    {/* Air Printing Detection */}
-                    <div className="space-y-2">
-                      <div className="flex items-center gap-3">
-                        <Checkbox
-                          checked={printOptions.airprint_detector}
-                          onChange={(checked) => handleToggle('airprint_detector', checked, printOptions.airprint_sensitivity)}
-                          disabled={!isConnected}
-                          loading={isToggling('airprint_detector')}
-                        />
-                        <span className="text-sm font-medium text-white">Air Printing Detection</span>
-                      </div>
-                      <p className="text-xs text-bambu-gray ml-8">
-                        Detects air printing caused by nozzle clogging or filament grinding.
-                      </p>
-                      <div className="flex items-center gap-3 ml-8">
-                        <span className="text-xs text-bambu-gray">Pausing Sensitivity:</span>
-                        <SensitivityDropdown
-                          value={printOptions.airprint_sensitivity || 'medium'}
-                          onChange={(value) => handleSensitivityChange('airprint_detector', value)}
-                          disabled={!isConnected || !printOptions.airprint_detector}
-                          loading={isSensitivityLoading('airprint_detector')}
-                        />
-                      </div>
-                    </div>
-                  </div>
-                </div>
-
-                <hr className="border-bambu-dark-tertiary" />
-
-                {/* Build Plate Detection Section */}
-                <div>
-                  <h3 className="text-base font-semibold text-white mb-1">Build Plate Detection</h3>
-                  <p className="text-xs text-bambu-gray mb-4">
-                    Ensures the build plate type and placement are correct.
-                  </p>
-                  <div className="space-y-2">
-                    <div className="flex items-center gap-3">
-                      <Checkbox
-                        checked={printOptions.buildplate_marker_detector}
-                        onChange={(checked) => handleToggle('buildplate_marker_detector', checked)}
-                        disabled={!isConnected}
-                        loading={isToggling('buildplate_marker_detector')}
-                      />
-                      <span className="text-sm font-medium text-white">Type Detection</span>
-                    </div>
-                    <p className="text-xs text-bambu-gray ml-8">
-                      Pauses printing when the detected build plate type does not match the selected one.
-                    </p>
-                  </div>
-                </div>
-
-                <hr className="border-bambu-dark-tertiary" />
-
-                {/* Other Options */}
-                <div className="space-y-5">
-                  {/* Auto-recover from step loss */}
-                  <div className="flex items-center gap-3">
-                    <Checkbox
-                      checked={printOptions.auto_recovery_step_loss}
-                      onChange={(checked) => handleToggle('auto_recovery_step_loss', checked)}
-                      disabled={!isConnected}
-                      loading={isToggling('auto_recovery_step_loss')}
-                    />
-                    <span className="text-sm font-medium text-white">Auto-recover from step loss</span>
-                  </div>
-
-                  {/* Store Sent Files on External Storage - Read-only (no MQTT command available) */}
-                  <div className="space-y-2">
-                    <div className="flex items-center gap-3">
-                      <Checkbox
-                        checked={status?.store_to_sdcard ?? false}
-                        onChange={() => {}}
-                        disabled={true}
-                      />
-                      <span className="text-sm font-medium text-white">Store Sent Files on External Storage</span>
-                      <span className="text-xs text-bambu-gray italic">(read-only)</span>
-                    </div>
-                    <p className="text-xs text-bambu-gray ml-8">
-                      Save the printing files initiated from Bambu Studio, Bambu Handy and MakerWorld on External Storage.
-                      This option can only be changed on the printer's touchscreen.
-                    </p>
-                  </div>
-                </div>
-              </>
-            )}
-          </div>
-
-        </CardContent>
-      </Card>
-    </div>
-  );
-}

+ 0 - 190
frontend/src/components/control/PrintStatus.tsx

@@ -1,190 +0,0 @@
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { useState } from 'react';
-import type { PrinterStatus } from '../../api/client';
-import { api, isConfirmationRequired } from '../../api/client';
-import { Pause, Square, Loader2 } from 'lucide-react';
-import { ConfirmModal } from '../ConfirmModal';
-
-interface PrintStatusProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-function formatFinishTime(seconds: number | null | undefined): string {
-  if (!seconds) return 'N/A';
-  const now = new Date();
-  const finish = new Date(now.getTime() + seconds * 1000);
-  return finish.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
-}
-
-export function PrintStatus({ printerId, status }: PrintStatusProps) {
-  const queryClient = useQueryClient();
-  const [confirmModal, setConfirmModal] = useState<{
-    action: string;
-    token: string;
-    warning: string;
-  } | null>(null);
-
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING';
-  const isPaused = status?.state === 'PAUSE';
-  const progress = status?.progress ?? 0;
-
-  const pauseMutation = useMutation({
-    mutationFn: () => api.pausePrint(printerId),
-    onSuccess: () => {
-      queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-    },
-  });
-
-  const resumeMutation = useMutation({
-    mutationFn: () => api.resumePrint(printerId),
-    onSuccess: () => {
-      queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-    },
-  });
-
-  const stopMutation = useMutation({
-    mutationFn: (token?: string) => api.stopPrint(printerId, token),
-    onSuccess: (result) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          action: 'stop',
-          token: result.token,
-          warning: result.warning,
-        });
-      } else {
-        queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-      }
-    },
-  });
-
-  const handlePauseResume = () => {
-    if (isPrinting) {
-      pauseMutation.mutate();
-    } else if (isPaused) {
-      resumeMutation.mutate();
-    }
-  };
-
-  const handleStop = () => {
-    stopMutation.mutate(undefined);
-  };
-
-  const handleConfirmStop = () => {
-    if (confirmModal) {
-      stopMutation.mutate(confirmModal.token);
-      setConfirmModal(null);
-    }
-  };
-
-  const isLoading = pauseMutation.isPending || resumeMutation.isPending || stopMutation.isPending;
-  const canControl = isConnected && (isPrinting || isPaused);
-
-  return (
-    <>
-      <div className="text-xs text-bambu-gray mb-3">Printing Progress</div>
-      <div className="flex gap-4 items-center">
-        {/* Thumbnail */}
-        <div className="w-20 h-20 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden">
-          {status?.subtask_name ? (
-            <img
-              src={`/api/v1/archives/thumbnail/${encodeURIComponent(status.subtask_name)}`}
-              alt=""
-              className="w-full h-full object-contain"
-              onError={(e) => {
-                (e.target as HTMLImageElement).style.display = 'none';
-                (e.target as HTMLImageElement).parentElement!.innerHTML = '<span class="text-xs text-bambu-gray">Bambu<br/>Lab</span>';
-              }}
-            />
-          ) : (
-            <span className="text-xs text-bambu-gray text-center">Bambu<br/>Lab</span>
-          )}
-        </div>
-
-        {/* Info */}
-        <div className="flex-1 min-w-0">
-          <div className="font-medium text-sm text-white truncate mb-0.5">
-            {status?.subtask_name || 'N/A'}
-          </div>
-          <div className={`text-sm mb-2 ${
-            status?.state === 'RUNNING' || status?.state === 'PAUSE'
-              ? 'text-bambu-green'
-              : 'text-bambu-gray'
-          }`}>
-            {status?.state || 'N/A'}
-          </div>
-          {/* Progress Bar */}
-          <div className="h-1 bg-bambu-dark-tertiary rounded-full overflow-hidden mb-2">
-            <div
-              className={`h-full transition-all duration-300 ${
-                status?.state === 'PAUSE' ? 'bg-yellow-500' : 'bg-bambu-green'
-              }`}
-              style={{ width: `${progress}%` }}
-            />
-          </div>
-          <div className="text-xs text-bambu-gray mb-1">
-            Layer: {status?.layer_num ?? 'N/A'} / {status?.total_layers ?? 'N/A'} &nbsp;&nbsp; {Math.round(progress)}%
-          </div>
-          <div className="text-xs text-bambu-gray">
-            Estimated finish time: {formatFinishTime(status?.remaining_time)}
-          </div>
-        </div>
-
-        {/* Control Buttons */}
-        <div className="flex gap-1.5 flex-shrink-0">
-          <button
-            onClick={handlePauseResume}
-            disabled={!canControl || isLoading}
-            className="w-8 h-8 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary flex items-center justify-center text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-            title={isPrinting ? 'Pause' : 'Resume'}
-          >
-            {pauseMutation.isPending || resumeMutation.isPending ? (
-              <Loader2 className="w-4 h-4 animate-spin" />
-            ) : (
-              <Pause className="w-4 h-4" />
-            )}
-          </button>
-          <button
-            onClick={handleStop}
-            disabled={!canControl || isLoading}
-            className="w-8 h-8 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary flex items-center justify-center text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-            title="Stop"
-          >
-            {stopMutation.isPending ? (
-              <Loader2 className="w-4 h-4 animate-spin" />
-            ) : (
-              <Square className="w-4 h-4" />
-            )}
-          </button>
-          <button
-            disabled={!canControl || isLoading}
-            className="w-8 h-8 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary flex items-center justify-center text-bambu-gray disabled:opacity-50 disabled:cursor-not-allowed"
-            title="Skip Objects"
-          >
-            <img src="/icons/skip-objects.svg" alt="Skip Objects" className="w-4 h-4 icon-theme" />
-          </button>
-        </div>
-      </div>
-
-      {/* Error Message */}
-      {(pauseMutation.error || resumeMutation.error || stopMutation.error) && (
-        <p className="mt-2 text-xs text-red-500">
-          {(pauseMutation.error || resumeMutation.error || stopMutation.error)?.message}
-        </p>
-      )}
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Stop"
-          message={confirmModal.warning}
-          confirmText="Stop Print"
-          variant="danger"
-          onConfirm={handleConfirmStop}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 192
frontend/src/components/control/PrinterPartsModal.tsx

@@ -1,192 +0,0 @@
-import { useEffect } from 'react';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { Printer, PrinterStatus } from '../../api/client';
-import { X, RefreshCw, Loader2 } from 'lucide-react';
-import { Card, CardContent } from '../Card';
-
-interface PrinterPartsModalProps {
-  printer: Printer;
-  status: PrinterStatus | null | undefined;
-  onClose: () => void;
-}
-
-// Convert API nozzle_type to display name
-// Bambu nozzle codes: SS = Stainless Steel, HS = Hardened Steel, HH = Hardened Steel High-flow
-function getNozzleTypeName(type: string): string {
-  const upperType = type.toUpperCase();
-
-  // Handle Bambu nozzle codes (e.g., HH01, HS00, SS00)
-  if (upperType.startsWith('HH')) {
-    return 'Hardened Steel';
-  }
-  if (upperType.startsWith('HS')) {
-    return 'Hardened Steel';
-  }
-  if (upperType.startsWith('SS')) {
-    return 'Stainless Steel';
-  }
-
-  // Handle full names from API
-  switch (type) {
-    case 'hardened_steel':
-      return 'Hardened Steel';
-    case 'stainless_steel':
-      return 'Stainless Steel';
-    default:
-      return type || 'Unknown';
-  }
-}
-
-// Determine flow type based on nozzle type code
-// HH = High-flow, HS/SS = Standard
-function getFlowType(type: string): string {
-  const upperType = type.toUpperCase();
-  if (upperType.startsWith('HH')) {
-    return 'High flow';
-  }
-  return 'Standard';
-}
-
-export function PrinterPartsModal({ printer, status, onClose }: PrinterPartsModalProps) {
-  const queryClient = useQueryClient();
-  const isDualNozzle = printer.nozzle_count > 1;
-
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') onClose();
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose]);
-
-  // Get nozzle data from status
-  // On H2D, both nozzles must be identical - if right nozzle is empty, copy from left
-  const leftNozzle = status?.nozzles?.[0];
-  const rightNozzleRaw = status?.nozzles?.[1];
-  const rightNozzle = (rightNozzleRaw?.nozzle_type || rightNozzleRaw?.nozzle_diameter)
-    ? rightNozzleRaw
-    : leftNozzle;
-
-  // Refresh mutation - sends pushall command to printer
-  const refreshMutation = useMutation({
-    mutationFn: () => api.refreshStatus(printer.id),
-    onSuccess: () => {
-      // Invalidate queries to get updated data
-      queryClient.invalidateQueries({ queryKey: ['printerStatuses'] });
-    },
-  });
-
-  const handleRefresh = () => {
-    refreshMutation.mutate();
-  };
-
-  return (
-    <div
-      className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
-      onClick={onClose}
-    >
-      <Card className="w-full max-w-2xl" onClick={(e: React.MouseEvent) => e.stopPropagation()}>
-        <CardContent className="p-0">
-          {/* Header */}
-          <div className="flex items-center justify-between px-4 py-3 border-b border-bambu-dark-tertiary">
-            <span className="text-sm font-medium text-white">Printer Parts</span>
-            <button
-              onClick={onClose}
-              className="p-1 rounded hover:bg-bambu-dark-tertiary text-bambu-gray hover:text-white"
-            >
-              <X className="w-4 h-4" />
-            </button>
-          </div>
-
-          {/* Content */}
-          <div className="p-6 space-y-6">
-            {/* Left Nozzle */}
-            <div>
-              <h3 className="text-base font-semibold text-white mb-3">
-                {isDualNozzle ? 'Left Nozzle' : 'Nozzle'}
-              </h3>
-              <div className="grid grid-cols-3 gap-4">
-                <div>
-                  <label className="block text-sm text-bambu-gray mb-1.5">Type</label>
-                  <div className="px-3 py-2 bg-bambu-dark rounded border border-bambu-dark-tertiary text-sm text-bambu-gray">
-                    {getNozzleTypeName(leftNozzle?.nozzle_type || '')}
-                  </div>
-                </div>
-                <div>
-                  <label className="block text-sm text-bambu-gray mb-1.5">Diameter</label>
-                  <div className="px-3 py-2 bg-bambu-dark rounded border border-bambu-dark-tertiary text-sm text-bambu-gray">
-                    {leftNozzle?.nozzle_diameter || '—'}
-                  </div>
-                </div>
-                <div>
-                  <label className="block text-sm text-bambu-gray mb-1.5">Flow</label>
-                  <div className="px-3 py-2 bg-bambu-dark rounded border border-bambu-dark-tertiary text-sm text-bambu-gray">
-                    {getFlowType(leftNozzle?.nozzle_type || '')}
-                  </div>
-                </div>
-              </div>
-            </div>
-
-            {/* Right Nozzle - only for dual nozzle printers */}
-            {isDualNozzle && (
-              <div>
-                <h3 className="text-base font-semibold text-white mb-3">Right Nozzle</h3>
-                <div className="grid grid-cols-3 gap-4">
-                  <div>
-                    <label className="block text-sm text-bambu-gray mb-1.5">Type</label>
-                    <div className="px-3 py-2 bg-bambu-dark rounded border border-bambu-dark-tertiary text-sm text-bambu-gray">
-                      {getNozzleTypeName(rightNozzle?.nozzle_type || '')}
-                    </div>
-                  </div>
-                  <div>
-                    <label className="block text-sm text-bambu-gray mb-1.5">Diameter</label>
-                    <div className="px-3 py-2 bg-bambu-dark rounded border border-bambu-dark-tertiary text-sm text-bambu-gray">
-                      {rightNozzle?.nozzle_diameter || '—'}
-                    </div>
-                  </div>
-                  <div>
-                    <label className="block text-sm text-bambu-gray mb-1.5">Flow</label>
-                    <div className="px-3 py-2 bg-bambu-dark rounded border border-bambu-dark-tertiary text-sm text-bambu-gray">
-                      {getFlowType(rightNozzle?.nozzle_type || '')}
-                    </div>
-                  </div>
-                </div>
-              </div>
-            )}
-
-            {/* Info text */}
-            <p className="text-sm text-bambu-gray">
-              Please change the nozzle settings on the printer.{' '}
-              <a
-                href="https://wiki.bambulab.com"
-                target="_blank"
-                rel="noopener noreferrer"
-                className="text-bambu-green hover:underline"
-              >
-                View wiki
-              </a>
-            </p>
-          </div>
-
-          {/* Footer */}
-          <div className="flex justify-end px-4 py-3 border-t border-bambu-dark-tertiary">
-            <button
-              onClick={handleRefresh}
-              disabled={refreshMutation.isPending}
-              className="flex items-center gap-2 px-4 py-2 bg-bambu-green hover:bg-bambu-green-dark disabled:opacity-50 text-white text-sm font-medium rounded"
-            >
-              {refreshMutation.isPending ? (
-                <Loader2 className="w-4 h-4 animate-spin" />
-              ) : (
-                <RefreshCw className="w-4 h-4" />
-              )}
-              Refresh
-            </button>
-          </div>
-        </CardContent>
-      </Card>
-    </div>
-  );
-}

+ 0 - 71
frontend/src/components/control/SpeedControl.tsx

@@ -1,71 +0,0 @@
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { Gauge, Loader2 } from 'lucide-react';
-
-interface SpeedControlProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-}
-
-const SPEED_MODES = [
-  { mode: 1, name: 'Silent', icon: '🔇' },
-  { mode: 2, name: 'Standard', icon: '⚡' },
-  { mode: 3, name: 'Sport', icon: '🚀' },
-  { mode: 4, name: 'Ludicrous', icon: '💨' },
-];
-
-export function SpeedControl({ printerId, status }: SpeedControlProps) {
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING';
-
-  // Note: Bambu printers don't report current speed mode via MQTT
-  // So we can't show which mode is currently active
-
-  const speedMutation = useMutation({
-    mutationFn: (mode: number) => api.setPrintSpeed(printerId, mode),
-  });
-
-  return (
-    <div className="bg-bambu-dark-secondary rounded-lg p-4">
-      <div className="flex items-center gap-2 mb-4">
-        <Gauge className="w-4 h-4 text-bambu-gray" />
-        <h3 className="text-sm font-medium">Print Speed</h3>
-        {speedMutation.isPending && (
-          <Loader2 className="w-4 h-4 animate-spin text-bambu-green" />
-        )}
-      </div>
-
-      <div className="grid grid-cols-4 gap-2">
-        {SPEED_MODES.map(({ mode, name, icon }) => (
-          <button
-            key={mode}
-            onClick={() => speedMutation.mutate(mode)}
-            disabled={!isConnected || !isPrinting || speedMutation.isPending}
-            className={`flex flex-col items-center gap-1 px-3 py-3 rounded-lg transition-colors ${
-              mode === 4
-                ? 'bg-red-500/20 text-red-400 hover:bg-red-500/30'
-                : 'bg-bambu-dark hover:bg-bambu-dark-tertiary'
-            } disabled:opacity-50 disabled:cursor-not-allowed`}
-            title={name}
-          >
-            <span className="text-lg">{icon}</span>
-            <span className="text-xs">{name}</span>
-          </button>
-        ))}
-      </div>
-
-      {!isPrinting && (
-        <p className="mt-3 text-xs text-bambu-gray text-center">
-          Speed control only available during print
-        </p>
-      )}
-
-      {speedMutation.error && (
-        <p className="mt-2 text-sm text-red-400">
-          {speedMutation.error.message}
-        </p>
-      )}
-    </div>
-  );
-}

+ 0 - 133
frontend/src/components/control/SpeedModal.tsx

@@ -1,133 +0,0 @@
-import { useState, useEffect } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { Printer, PrinterStatus } from '../../api/client';
-import { X, AlertTriangle } from 'lucide-react';
-
-interface SpeedModalProps {
-  printer: Printer;
-  status: PrinterStatus | null | undefined;
-  onClose: () => void;
-}
-
-type SpeedMode = 1 | 2 | 3 | 4;
-
-interface SpeedOption {
-  mode: SpeedMode;
-  name: string;
-  description: string;
-  percentage: string;
-}
-
-const SPEED_OPTIONS: SpeedOption[] = [
-  { mode: 1, name: 'Silent', description: 'Quieter printing, slower speed', percentage: '50%' },
-  { mode: 2, name: 'Standard', description: 'Balanced speed and quality', percentage: '100%' },
-  { mode: 3, name: 'Sport', description: 'Faster printing, moderate noise', percentage: '124%' },
-  { mode: 4, name: 'Ludicrous', description: 'Maximum speed', percentage: '166%' },
-];
-
-export function SpeedModal({ printer, status, onClose }: SpeedModalProps) {
-  const isConnected = status?.connected ?? false;
-  const isPrinting = status?.state === 'RUNNING' || status?.state === 'PRINTING';
-  // Speed can only be changed during a print
-  const isDisabled = !isConnected || !isPrinting;
-
-  // Initialize from printer status
-  const initialSpeed = (status?.speed_level ?? 2) as SpeedMode;
-  const [selectedSpeed, setSelectedSpeed] = useState<SpeedMode>(initialSpeed);
-
-  const speedMutation = useMutation({
-    mutationFn: (mode: number) => api.setPrintSpeed(printer.id, mode),
-  });
-
-  const handleSpeedChange = (mode: SpeedMode) => {
-    if (mode === selectedSpeed || isDisabled) return;
-    setSelectedSpeed(mode);
-    speedMutation.mutate(mode);
-  };
-
-  // Close on Escape key
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Escape') {
-        onClose();
-      }
-    };
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [onClose]);
-
-  return (
-    <div
-      className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4"
-      onClick={onClose}
-    >
-      <div
-        className="relative w-full max-w-lg bg-bambu-dark-secondary rounded-2xl shadow-2xl border border-bambu-dark-tertiary overflow-hidden"
-        onClick={(e) => e.stopPropagation()}
-      >
-        {/* Header */}
-        <div className="flex items-center justify-between px-5 py-4 bg-bambu-dark border-b border-bambu-dark-tertiary">
-          <div className="flex items-center gap-3">
-            <div className="w-8 h-8 rounded-lg bg-bambu-green/20 flex items-center justify-center">
-              <img src="/icons/speed.svg" alt="" className="w-5 h-5 icon-green" />
-            </div>
-            <span className="text-base font-semibold text-white">Print Speed</span>
-          </div>
-          <button
-            onClick={onClose}
-            className="w-8 h-8 rounded-lg flex items-center justify-center text-bambu-gray hover:bg-bambu-dark-tertiary hover:text-white transition-colors"
-          >
-            <X className="w-5 h-5" />
-          </button>
-        </div>
-
-        {/* Content */}
-        <div className="p-5 space-y-4">
-          {/* Info Banner */}
-          {!isPrinting ? (
-            <div className="flex items-center gap-2 p-3 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg">
-              <AlertTriangle className="w-4 h-4 text-bambu-gray flex-shrink-0" />
-              <span className="text-xs text-bambu-gray">
-                No print in progress. Speed can only be changed during printing.
-              </span>
-            </div>
-          ) : (
-            <div className="flex items-center gap-2 p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
-              <AlertTriangle className="w-4 h-4 text-yellow-500 flex-shrink-0" />
-              <span className="text-xs text-yellow-500">
-                Speed changes take effect immediately.
-              </span>
-            </div>
-          )}
-
-          {/* Speed Options */}
-          <div className="space-y-2">
-            {SPEED_OPTIONS.map((option) => (
-              <button
-                key={option.mode}
-                onClick={() => handleSpeedChange(option.mode)}
-                disabled={isDisabled || speedMutation.isPending}
-                className={`w-full p-4 rounded-xl flex items-center justify-between transition-all disabled:opacity-50 disabled:cursor-not-allowed ${
-                  selectedSpeed === option.mode
-                    ? 'bg-bambu-green text-white shadow-lg'
-                    : 'bg-bambu-dark text-bambu-gray hover:text-white hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary'
-                }`}
-              >
-                <div className="flex flex-col items-start">
-                  <span className="font-medium">{option.name}</span>
-                  <span className={`text-xs ${selectedSpeed === option.mode ? 'text-white/80' : 'text-bambu-gray'}`}>
-                    {option.description}
-                  </span>
-                </div>
-                <span className={`text-lg font-semibold ${selectedSpeed === option.mode ? 'text-white' : 'text-bambu-gray'}`}>
-                  {option.percentage}
-                </span>
-              </button>
-            ))}
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}

+ 0 - 270
frontend/src/components/control/TemperatureColumn.tsx

@@ -1,270 +0,0 @@
-import { useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { Printer, PrinterStatus } from '../../api/client';
-import { AirConditionModal } from './AirConditionModal';
-import { SpeedModal } from './SpeedModal';
-
-interface Temperatures {
-  bed?: number;
-  bed_target?: number;
-  bed_heating?: boolean;
-  nozzle?: number;
-  nozzle_target?: number;
-  nozzle_heating?: boolean;
-  nozzle_2?: number;
-  nozzle_2_target?: number;
-  nozzle_2_heating?: boolean;
-  chamber?: number;
-  chamber_target?: number;
-  chamber_heating?: boolean;
-}
-
-interface TemperatureColumnProps {
-  printer: Printer;
-  status: PrinterStatus | null | undefined;
-  disabled?: boolean;
-}
-
-type EditingField = 'nozzle' | 'nozzle_2' | 'bed' | 'chamber' | null;
-
-export function TemperatureColumn({ printer, status, disabled = false }: TemperatureColumnProps) {
-  const temps = (status?.temperatures ?? {}) as Temperatures;
-  const isDualNozzle = printer.nozzle_count > 1;
-  const isConnected = (status?.connected ?? false) && !disabled;
-
-  const [editing, setEditing] = useState<EditingField>(null);
-  const [editValue, setEditValue] = useState('');
-  const [showAirConditionModal, setShowAirConditionModal] = useState(false);
-  const [showSpeedModal, setShowSpeedModal] = useState(false);
-
-  const bedMutation = useMutation({
-    mutationFn: (target: number) => api.setBedTemperature(printer.id, target),
-  });
-
-  const nozzleMutation = useMutation({
-    mutationFn: ({ target, nozzle }: { target: number; nozzle: number }) =>
-      api.setNozzleTemperature(printer.id, target, nozzle),
-  });
-
-  const chamberMutation = useMutation({
-    mutationFn: (target: number) => api.setChamberTemperature(printer.id, target),
-  });
-
-  const lightMutation = useMutation({
-    mutationFn: (on: boolean) => api.setChamberLight(printer.id, on),
-  });
-
-  const startEditing = (field: EditingField, currentValue: number) => {
-    if (!isConnected) return;
-    setEditing(field);
-    setEditValue(String(Math.round(currentValue)));
-  };
-
-  const cancelEditing = () => {
-    setEditing(null);
-    setEditValue('');
-  };
-
-  const submitEdit = () => {
-    const target = parseInt(editValue, 10);
-    if (isNaN(target) || target < 0) {
-      cancelEditing();
-      return;
-    }
-
-    if (editing === 'bed') {
-      bedMutation.mutate(target);
-    } else if (editing === 'nozzle') {
-      // nozzle field = LEFT nozzle display
-      // H2D: LEFT is T1 (index 1), single nozzle: index 0
-      nozzleMutation.mutate({ target, nozzle: isDualNozzle ? 1 : 0 });
-    } else if (editing === 'nozzle_2') {
-      // nozzle_2 field = RIGHT nozzle display
-      // H2D: RIGHT is T0/default (index 0)
-      nozzleMutation.mutate({ target, nozzle: 0 });
-    } else if (editing === 'chamber') {
-      chamberMutation.mutate(target);
-    }
-    cancelEditing();
-  };
-
-  const handleKeyDown = (e: React.KeyboardEvent) => {
-    if (e.key === 'Enter') {
-      submitEdit();
-    } else if (e.key === 'Escape') {
-      cancelEditing();
-    }
-  };
-
-  const isDisabled = !isConnected;
-
-  // Use live heating state from MQTT
-  const isNozzleHeating = temps.nozzle_heating ?? false;
-  const isNozzle2Heating = temps.nozzle_2_heating ?? false;
-  const isBedHeating = temps.bed_heating ?? false;
-  const isChamberHeating = temps.chamber_heating ?? false;
-
-  const renderTargetTemp = (
-    field: EditingField,
-    targetValue: number
-  ) => {
-    if (editing === field) {
-      return (
-        <input
-          type="text"
-          inputMode="numeric"
-          pattern="[0-9]*"
-          value={editValue}
-          onChange={(e) => {
-            // Only allow numeric input
-            const val = e.target.value.replace(/[^0-9]/g, '');
-            setEditValue(val);
-          }}
-          onBlur={submitEdit}
-          onKeyDown={handleKeyDown}
-          autoFocus
-          className="w-12 text-sm bg-bambu-dark border border-bambu-green rounded px-1 py-0.5 text-white text-center [appearance:textfield]"
-        />
-      );
-    }
-    return (
-      <button
-        onClick={() => startEditing(field, targetValue)}
-        disabled={isDisabled}
-        className="text-sm text-bambu-gray hover:text-bambu-green disabled:hover:text-bambu-gray disabled:cursor-not-allowed"
-        title={isDisabled ? 'Printer not connected' : 'Click to set target temperature'}
-      >
-        /{Math.round(targetValue)} °C
-      </button>
-    );
-  };
-
-  return (
-    <>
-    <div className="flex flex-col justify-evenly min-w-[150px] pr-5 border-r border-bambu-dark-tertiary">
-      {/* Nozzle 1 (Left) */}
-      <div className="flex items-center gap-1.5">
-        <div className="w-5 h-5 flex items-center justify-center flex-shrink-0">
-          <img
-            src="/icons/hotend.svg"
-            alt=""
-            className={`w-5 ${isNozzleHeating ? 'icon-heating' : 'icon-theme'}`}
-          />
-        </div>
-        {isDualNozzle && (
-          <span className="text-[11px] font-semibold text-bambu-green bg-bambu-green/20 px-1.5 py-0.5 rounded min-w-[18px] text-center flex-shrink-0">
-            L
-          </span>
-        )}
-        <span className="text-lg font-medium text-white">{Math.round(temps.nozzle ?? 0)}</span>
-        {renderTargetTemp('nozzle', temps.nozzle_target ?? 0)}
-      </div>
-
-      {/* Nozzle 2 (Right) - only for dual nozzle */}
-      {isDualNozzle && (
-        <div className="flex items-center gap-1.5">
-          <div className="w-5 h-5 flex items-center justify-center flex-shrink-0">
-            <img
-              src="/icons/hotend.svg"
-              alt=""
-              className={`w-5 ${isNozzle2Heating ? 'icon-heating' : 'icon-theme'}`}
-            />
-          </div>
-          <span className="text-[11px] font-semibold text-bambu-green bg-bambu-green/20 px-1.5 py-0.5 rounded min-w-[18px] text-center flex-shrink-0">
-            R
-          </span>
-          <span className="text-lg font-medium text-white">{Math.round(temps.nozzle_2 ?? 0)}</span>
-          {renderTargetTemp('nozzle_2', temps.nozzle_2_target ?? 0)}
-        </div>
-      )}
-
-      {/* Bed */}
-      <div className="flex items-center gap-1.5">
-        <div className="w-5 h-5 flex items-center justify-center flex-shrink-0">
-          <img
-            src="/icons/heatbed.svg"
-            alt=""
-            className={`w-5 ${isBedHeating ? 'icon-heating' : 'icon-theme'}`}
-          />
-        </div>
-        {isDualNozzle && <span className="min-w-[18px] flex-shrink-0" />}
-        <span className="text-lg font-medium text-white">{Math.round(temps.bed ?? 0)}</span>
-        {renderTargetTemp('bed', temps.bed_target ?? 0)}
-      </div>
-
-      {/* Chamber - editable target */}
-      <div className="flex items-center gap-1.5">
-        <div className="w-5 h-5 flex items-center justify-center flex-shrink-0">
-          <img src="/icons/chamber.svg" alt="" className={`w-5 ${isChamberHeating ? 'icon-heating' : 'icon-theme'}`} />
-        </div>
-        {isDualNozzle && <span className="min-w-[18px] flex-shrink-0" />}
-        <span className="text-lg font-medium text-white">{Math.round(temps.chamber ?? 0)}</span>
-        {renderTargetTemp('chamber', temps.chamber_target ?? 0)}
-      </div>
-
-      {/* Air Condition - full width button with mode indicator */}
-      <button
-        onClick={() => setShowAirConditionModal(true)}
-        disabled={isDisabled}
-        className="flex items-center justify-between w-full px-3 py-4 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-      >
-        <div className="flex items-center gap-2">
-          <img src="/icons/ventilation.svg" alt="" className="w-5 h-5 icon-theme" />
-          <span className="text-xs text-bambu-gray">A/C</span>
-        </div>
-        <span className={`text-[10px] px-1.5 py-0.5 rounded ${
-          status?.airduct_mode === 1
-            ? 'bg-orange-500/20 text-orange-400'
-            : 'bg-blue-500/20 text-blue-400'
-        }`}>
-          {status?.airduct_mode === 1 ? 'Heat' : 'Cool'}
-        </span>
-      </button>
-
-      {/* Speed & Lamp - half width each */}
-      <div className="flex items-center gap-2">
-        {/* Speed */}
-        <button
-          onClick={() => setShowSpeedModal(true)}
-          disabled={isDisabled}
-          className="flex-1 flex flex-col items-center py-2 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-        >
-          <img src="/icons/speed.svg" alt="" className="w-5 h-5 icon-theme" />
-          <span className="text-[10px] text-bambu-gray mt-0.5">
-            {status?.speed_level === 1 ? '50%' : status?.speed_level === 3 ? '124%' : status?.speed_level === 4 ? '166%' : '100%'}
-          </span>
-        </button>
-
-        {/* Lamp */}
-        <button
-          onClick={() => lightMutation.mutate(!(status?.chamber_light ?? false))}
-          disabled={isDisabled || lightMutation.isPending}
-          className="flex-1 flex flex-col items-center py-2 rounded-md bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary border border-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-        >
-          <img src="/icons/lamp.svg" alt="" className={`w-5 h-5 ${status?.chamber_light ? 'icon-green' : 'icon-theme'}`} />
-          <span className="text-[10px] text-bambu-gray mt-0.5">Lamp</span>
-        </button>
-      </div>
-    </div>
-
-    {/* Air Condition Modal */}
-    {showAirConditionModal && (
-      <AirConditionModal
-        printer={printer}
-        status={status}
-        onClose={() => setShowAirConditionModal(false)}
-      />
-    )}
-
-    {/* Speed Modal */}
-    {showSpeedModal && (
-      <SpeedModal
-        printer={printer}
-        status={status}
-        onClose={() => setShowSpeedModal(false)}
-      />
-    )}
-    </>
-  );
-}

+ 0 - 246
frontend/src/components/control/TemperaturePanel.tsx

@@ -1,246 +0,0 @@
-import { useState } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api, isConfirmationRequired } from '../../api/client';
-import type { PrinterStatus } from '../../api/client';
-import { Thermometer, Loader2, Plus, Minus } from 'lucide-react';
-import { ConfirmModal } from '../ConfirmModal';
-
-// Extended temperature interface for dual nozzle support
-interface Temperatures {
-  bed?: number;
-  bed_target?: number;
-  nozzle?: number;
-  nozzle_target?: number;
-  nozzle_2?: number;
-  nozzle_2_target?: number;
-  chamber?: number;
-}
-
-interface TemperaturePanelProps {
-  printerId: number;
-  status: PrinterStatus | null | undefined;
-  nozzleCount: number;
-}
-
-interface TempPreset {
-  name: string;
-  bed: number;
-  nozzle: number;
-}
-
-const PRESETS: TempPreset[] = [
-  { name: 'Off', bed: 0, nozzle: 0 },
-  { name: 'PLA', bed: 55, nozzle: 220 },
-  { name: 'PETG', bed: 70, nozzle: 250 },
-  { name: 'ABS', bed: 90, nozzle: 260 },
-  { name: 'TPU', bed: 50, nozzle: 230 },
-];
-
-export function TemperaturePanel({ printerId, status, nozzleCount }: TemperaturePanelProps) {
-  const [confirmModal, setConfirmModal] = useState<{
-    type: 'bed' | 'nozzle';
-    target: number;
-    nozzle?: number;
-    token: string;
-    warning: string;
-  } | null>(null);
-
-  const isConnected = status?.connected ?? false;
-  const temps = (status?.temperatures ?? {}) as Temperatures;
-
-  const bedMutation = useMutation({
-    mutationFn: ({ target, token }: { target: number; token?: string }) =>
-      api.setBedTemperature(printerId, target, token),
-    onSuccess: (result, variables) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          type: 'bed',
-          target: variables.target,
-          token: result.token,
-          warning: result.warning,
-        });
-      }
-    },
-  });
-
-  const nozzleMutation = useMutation({
-    mutationFn: ({ target, nozzle, token }: { target: number; nozzle: number; token?: string }) =>
-      api.setNozzleTemperature(printerId, target, nozzle, token),
-    onSuccess: (result, variables) => {
-      if (isConfirmationRequired(result)) {
-        setConfirmModal({
-          type: 'nozzle',
-          target: variables.target,
-          nozzle: variables.nozzle,
-          token: result.token,
-          warning: result.warning,
-        });
-      }
-    },
-  });
-
-  const handleConfirm = () => {
-    if (confirmModal) {
-      if (confirmModal.type === 'bed') {
-        bedMutation.mutate({ target: confirmModal.target, token: confirmModal.token });
-      } else {
-        nozzleMutation.mutate({
-          target: confirmModal.target,
-          nozzle: confirmModal.nozzle ?? 0,
-          token: confirmModal.token,
-        });
-      }
-      setConfirmModal(null);
-    }
-  };
-
-  const handlePreset = (preset: TempPreset) => {
-    bedMutation.mutate({ target: preset.bed });
-    nozzleMutation.mutate({ target: preset.nozzle, nozzle: 0 });
-    if (nozzleCount > 1) {
-      nozzleMutation.mutate({ target: preset.nozzle, nozzle: 1 });
-    }
-  };
-
-  const adjustTemp = (type: 'bed' | 'nozzle', delta: number, nozzle = 0) => {
-    const currentTarget =
-      type === 'bed'
-        ? (temps.bed_target ?? 0)
-        : nozzle === 0
-        ? (temps.nozzle_target ?? 0)
-        : (temps.nozzle_2_target ?? 0);
-    const newTarget = Math.max(0, Math.min(type === 'bed' ? 120 : 300, currentTarget + delta));
-
-    if (type === 'bed') {
-      bedMutation.mutate({ target: newTarget });
-    } else {
-      nozzleMutation.mutate({ target: newTarget, nozzle });
-    }
-  };
-
-  return (
-    <>
-      <div className="bg-bambu-dark-secondary rounded-lg p-4">
-        <div className="flex items-center gap-2 mb-4">
-          <Thermometer className="w-4 h-4 text-bambu-gray" />
-          <h3 className="text-sm font-medium">Temperatures</h3>
-        </div>
-
-        {/* Presets */}
-        <div className="flex gap-2 mb-4">
-          {PRESETS.map((preset) => (
-            <button
-              key={preset.name}
-              onClick={() => handlePreset(preset)}
-              disabled={!isConnected}
-              className="flex-1 px-2 py-1.5 text-xs rounded bg-bambu-dark-tertiary text-bambu-gray hover:bg-bambu-dark hover:text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              {preset.name}
-            </button>
-          ))}
-        </div>
-
-        {/* Bed Temperature */}
-        <div className="mb-4 p-3 rounded bg-bambu-dark">
-          <div className="flex items-center justify-between mb-2">
-            <span className="text-sm text-bambu-gray">Bed</span>
-            <span className="text-sm font-mono">
-              <span className="text-orange-400">{Math.round(temps.bed ?? 0)}°C</span>
-              <span className="text-bambu-gray mx-1">/</span>
-              <span className="text-white">{Math.round(temps.bed_target ?? 0)}°C</span>
-            </span>
-          </div>
-          <div className="flex items-center gap-2">
-            <button
-              onClick={() => adjustTemp('bed', -5)}
-              disabled={!isConnected || bedMutation.isPending}
-              className="p-2 rounded bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              <Minus className="w-4 h-4" />
-            </button>
-            <div className="flex-1 h-2 bg-bambu-dark-tertiary rounded-full overflow-hidden">
-              <div
-                className="h-full bg-orange-500 transition-all"
-                style={{ width: `${Math.min(100, ((temps.bed ?? 0) / 120) * 100)}%` }}
-              />
-            </div>
-            <button
-              onClick={() => adjustTemp('bed', 5)}
-              disabled={!isConnected || bedMutation.isPending}
-              className="p-2 rounded bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              <Plus className="w-4 h-4" />
-            </button>
-            {bedMutation.isPending && <Loader2 className="w-4 h-4 animate-spin" />}
-          </div>
-        </div>
-
-        {/* Nozzle Temperature(s) */}
-        {[0, ...(nozzleCount > 1 ? [1] : [])].map((nozzle) => {
-          const current = nozzle === 0 ? temps.nozzle : temps.nozzle_2;
-          const target = nozzle === 0 ? temps.nozzle_target : temps.nozzle_2_target;
-
-          return (
-            <div key={nozzle} className="mb-4 p-3 rounded bg-bambu-dark">
-              <div className="flex items-center justify-between mb-2">
-                <span className="text-sm text-bambu-gray">
-                  Nozzle {nozzleCount > 1 ? nozzle + 1 : ''}
-                </span>
-                <span className="text-sm font-mono">
-                  <span className="text-red-400">{Math.round(current ?? 0)}°C</span>
-                  <span className="text-bambu-gray mx-1">/</span>
-                  <span className="text-white">{Math.round(target ?? 0)}°C</span>
-                </span>
-              </div>
-              <div className="flex items-center gap-2">
-                <button
-                  onClick={() => adjustTemp('nozzle', -5, nozzle)}
-                  disabled={!isConnected || nozzleMutation.isPending}
-                  className="p-2 rounded bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-                >
-                  <Minus className="w-4 h-4" />
-                </button>
-                <div className="flex-1 h-2 bg-bambu-dark-tertiary rounded-full overflow-hidden">
-                  <div
-                    className="h-full bg-red-500 transition-all"
-                    style={{ width: `${Math.min(100, ((current ?? 0) / 300) * 100)}%` }}
-                  />
-                </div>
-                <button
-                  onClick={() => adjustTemp('nozzle', 5, nozzle)}
-                  disabled={!isConnected || nozzleMutation.isPending}
-                  className="p-2 rounded bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed"
-                >
-                  <Plus className="w-4 h-4" />
-                </button>
-                {nozzleMutation.isPending && <Loader2 className="w-4 h-4 animate-spin" />}
-              </div>
-            </div>
-          );
-        })}
-
-        {/* Chamber Temperature (read-only) */}
-        {temps.chamber !== undefined && (
-          <div className="p-3 rounded bg-bambu-dark">
-            <div className="flex items-center justify-between">
-              <span className="text-sm text-bambu-gray">Chamber</span>
-              <span className="text-sm font-mono text-blue-400">{Math.round(temps.chamber)}°C</span>
-            </div>
-          </div>
-        )}
-      </div>
-
-      {/* Confirmation Modal */}
-      {confirmModal && (
-        <ConfirmModal
-          title="Confirm Temperature"
-          message={confirmModal.warning}
-          confirmText="Set Temperature"
-          variant="warning"
-          onConfirm={handleConfirm}
-          onCancel={() => setConfirmModal(null)}
-        />
-      )}
-    </>
-  );
-}

+ 0 - 377
frontend/src/components/control/useAmsOperations.ts

@@ -1,377 +0,0 @@
-import { useState, useRef, useCallback, useEffect } from 'react';
-import { useMutation } from '@tanstack/react-query';
-import { api } from '../../api/client';
-import type { AMSUnit } from '../../api/client';
-
-/**
- * AMS Operation State Machine
- *
- * States:
- * - IDLE: No operation in progress, all buttons enabled
- * - REFRESHING: RFID refresh in progress for a specific slot
- * - LOADING: Filament load in progress
- * - UNLOADING: Filament unload in progress
- *
- * Completion detection:
- * - REFRESH: AMS tray data changes (tag_uid, tray_uuid, etc.) OR timeout (15s)
- * - LOAD/UNLOAD: ams_status_main transitions from 1 (filament_change) to 0 (idle) OR timeout (60s)
- *
- * Rules:
- * - Only one operation at a time
- * - All operations have timeout fallback
- * - Operation can be cancelled/reset manually
- */
-
-export type OperationState = 'IDLE' | 'REFRESHING' | 'LOADING' | 'UNLOADING';
-
-export interface RefreshTarget {
-  amsId: number;
-  trayId: number;
-}
-
-export interface OperationContext {
-  // For REFRESHING: which slot is being refreshed
-  refreshTarget?: RefreshTarget;
-  // For LOADING: target tray ID we're loading
-  loadTargetTrayId?: number;
-  // Timestamp when operation started
-  startTime: number;
-}
-
-interface UseAmsOperationsProps {
-  printerId: number;
-  amsUnits: AMSUnit[];
-  amsStatusMain: number;
-  trayNow: number;
-  lastAmsUpdate: number;  // Backend timestamp for detecting AMS data updates
-  onToast: (message: string, type: 'success' | 'error') => void;
-}
-
-interface UseAmsOperationsReturn {
-  // Current state
-  state: OperationState;
-  context: OperationContext | null;
-
-  // Operation triggers
-  startRefresh: (amsId: number, trayId: number) => void;
-  startLoad: (trayId: number, extruderId?: number) => void;
-  startUnload: () => void;
-
-  // Manual reset (e.g., for retry)
-  reset: () => void;
-
-  // Derived state helpers
-  isOperationInProgress: boolean;
-  isRefreshingSlot: (amsId: number, trayId: number) => boolean;
-
-  // For FilamentChangeCard - which type of operation
-  isLoadOperation: boolean;
-  loadTargetTrayId: number | null;
-  // Last initiated operation type - persists after state reset
-  // Used to determine card type when MQTT shows change but our state is IDLE
-  lastOperationType: 'load' | 'unload' | null;
-
-  // Mutation error states (for UI feedback)
-  loadError: Error | null;
-  unloadError: Error | null;
-  refreshError: Error | null;
-}
-
-// Timeouts for different operations
-const REFRESH_TIMEOUT_MS = 15000; // 15 seconds for RFID refresh
-const FILAMENT_CHANGE_TIMEOUT_MS = 120000; // 2 minutes for load/unload (these can take a while with heating)
-
-export function useAmsOperations({
-  printerId,
-  amsUnits,
-  amsStatusMain,
-  trayNow,
-  lastAmsUpdate,
-  onToast,
-}: UseAmsOperationsProps): UseAmsOperationsReturn {
-  const [state, setState] = useState<OperationState>('IDLE');
-  const [context, setContext] = useState<OperationContext | null>(null);
-  // Track the last operation type (load vs unload) - persists after state reset
-  // This helps show correct card type when MQTT shows filament change but our state is IDLE
-  // Using state instead of ref so changes trigger re-renders in consuming components
-  const [lastOperationType, setLastOperationType] = useState<'load' | 'unload' | null>(null);
-
-  // Track previous values for transition detection
-  const prevAmsStatusMainRef = useRef(amsStatusMain);
-  // Track initial tray data signature for detecting changes during refresh
-  const refreshInitialDataRef = useRef<string>('');
-  // Track initial lastAmsUpdate value at start of refresh (to detect when new data arrives)
-  const refreshInitialAmsUpdateRef = useRef<number>(0);
-
-  // Timeout ref for cleanup
-  const timeoutRef = useRef<NodeJS.Timeout | null>(null);
-
-  // Clear any pending timeout
-  const clearOperationTimeout = useCallback(() => {
-    if (timeoutRef.current) {
-      clearTimeout(timeoutRef.current);
-      timeoutRef.current = null;
-    }
-  }, []);
-
-  // Reset to IDLE state
-  const reset = useCallback(() => {
-    clearOperationTimeout();
-    setState('IDLE');
-    setContext(null);
-    refreshInitialDataRef.current = '';
-    refreshInitialAmsUpdateRef.current = 0;
-  }, [clearOperationTimeout]);
-
-  // === Mutations ===
-
-  const refreshMutation = useMutation({
-    mutationFn: ({ amsId, trayId }: { amsId: number; trayId: number }) =>
-      api.refreshAmsTray(printerId, amsId, trayId),
-    onSuccess: (data) => {
-      if (data.success) {
-        onToast(data.message || 'RFID refresh started', 'success');
-      } else {
-        onToast(data.message || 'Failed to refresh tray', 'error');
-        reset();
-      }
-    },
-    onError: (error) => {
-      console.error('[useAmsOperations] Refresh error:', error);
-      onToast('Failed to refresh tray', 'error');
-      reset();
-    },
-  });
-
-  const loadMutation = useMutation({
-    mutationFn: ({ trayId, extruderId }: { trayId: number; extruderId?: number }) =>
-      api.amsLoadFilament(printerId, trayId, extruderId),
-    onSuccess: (data) => {
-      console.log('[useAmsOperations] Load request sent:', data);
-      // Don't reset here - wait for ams_status_main transition
-    },
-    onError: (error) => {
-      console.error('[useAmsOperations] Load error:', error);
-      reset();
-    },
-  });
-
-  const unloadMutation = useMutation({
-    mutationFn: () => api.amsUnloadFilament(printerId),
-    onSuccess: (data) => {
-      console.log('[useAmsOperations] Unload request sent:', data);
-      // Don't reset here - wait for ams_status_main transition
-    },
-    onError: (error) => {
-      console.error('[useAmsOperations] Unload error:', error);
-      reset();
-    },
-  });
-
-  // === Operation Triggers ===
-
-  const startRefresh = useCallback((amsId: number, trayId: number) => {
-    if (state !== 'IDLE') {
-      console.log('[useAmsOperations] Cannot start refresh - operation in progress:', state);
-      return;
-    }
-
-    // Capture current tray data to detect changes
-    const unit = amsUnits.find(u => u.id === amsId);
-    const tray = unit?.tray?.find(t => t.id === trayId);
-    if (tray) {
-      refreshInitialDataRef.current = JSON.stringify({
-        tag_uid: tray.tag_uid,
-        tray_uuid: tray.tray_uuid,
-        tray_type: tray.tray_type,
-        tray_color: tray.tray_color,
-      });
-    }
-
-    // Capture initial lastAmsUpdate to detect when NEW data arrives
-    refreshInitialAmsUpdateRef.current = lastAmsUpdate;
-
-    const startTime = Date.now();
-    console.log(`[useAmsOperations] Starting refresh: AMS ${amsId}, Tray ${trayId}, startTime=${startTime}, initialAmsUpdate=${lastAmsUpdate}`);
-
-    setState('REFRESHING');
-    setContext({ refreshTarget: { amsId, trayId }, startTime });
-
-    // Set timeout
-    timeoutRef.current = setTimeout(() => {
-      console.log(`[useAmsOperations] Refresh timeout for AMS ${amsId} tray ${trayId}`);
-      reset();
-    }, REFRESH_TIMEOUT_MS);
-
-    refreshMutation.mutate({ amsId, trayId });
-  }, [state, amsUnits, lastAmsUpdate, reset, refreshMutation]);
-
-  const startLoad = useCallback((trayId: number, extruderId?: number) => {
-    if (state !== 'IDLE') {
-      console.log('[useAmsOperations] Cannot start load - operation in progress:', state);
-      return;
-    }
-
-    console.log(`[useAmsOperations] Starting load: tray ${trayId}, extruder ${extruderId}`);
-
-    const startTime = Date.now();
-    setState('LOADING');
-    setContext({ loadTargetTrayId: trayId, startTime });
-    setLastOperationType('load'); // Remember this was a load operation
-
-    // Set timeout
-    timeoutRef.current = setTimeout(() => {
-      console.log(`[useAmsOperations] Load timeout for tray ${trayId}`);
-      reset();
-    }, FILAMENT_CHANGE_TIMEOUT_MS);
-
-    loadMutation.mutate({ trayId, extruderId });
-  }, [state, reset, loadMutation]);
-
-  const startUnload = useCallback(() => {
-    if (state !== 'IDLE') {
-      console.log('[useAmsOperations] Cannot start unload - operation in progress:', state);
-      return;
-    }
-
-    console.log('[useAmsOperations] Starting unload');
-
-    const startTime = Date.now();
-    setState('UNLOADING');
-    setContext({ startTime });
-    setLastOperationType('unload'); // Remember this was an unload operation
-
-    // Set timeout
-    timeoutRef.current = setTimeout(() => {
-      console.log('[useAmsOperations] Unload timeout');
-      reset();
-    }, FILAMENT_CHANGE_TIMEOUT_MS);
-
-    unloadMutation.mutate();
-  }, [state, reset, unloadMutation]);
-
-  // === Completion Detection ===
-
-  // Detect REFRESH completion by waiting for new AMS data to arrive
-  // RFID read takes 5-10 seconds. We detect completion when:
-  // 1. Data changed (new/different spool detected) - complete after minimum 1s
-  // 2. lastAmsUpdate timestamp changed from initial AND elapsed > 5 seconds
-  //    This means a new AMS data packet arrived after the RFID read should be done
-  useEffect(() => {
-    if (state !== 'REFRESHING' || !context?.refreshTarget) return;
-
-    const { amsId, trayId } = context.refreshTarget;
-    const elapsed = Date.now() - context.startTime;
-
-    // Get current tray data
-    const unit = amsUnits.find(u => u.id === amsId);
-    const tray = unit?.tray?.find(t => t.id === trayId);
-    if (!tray) return;
-
-    const currentData = JSON.stringify({
-      tag_uid: tray.tag_uid,
-      tray_uuid: tray.tray_uuid,
-      tray_type: tray.tray_type,
-      tray_color: tray.tray_color,
-    });
-
-    // Check if data changed (new spool detected)
-    const dataChanged = refreshInitialDataRef.current && currentData !== refreshInitialDataRef.current;
-
-    // Check if lastAmsUpdate changed from when we started
-    const amsUpdateChanged = lastAmsUpdate !== refreshInitialAmsUpdateRef.current;
-
-    // Primary completion: data changed (new spool detected) - complete quickly
-    if (dataChanged && elapsed > 1000) {
-      console.log(`[useAmsOperations] Refresh complete: data changed for AMS ${amsId} tray ${trayId} (took ${elapsed}ms)`);
-      reset();
-      return;
-    }
-
-    // Secondary completion: new AMS update received after minimum wait time
-    // Wait 8 seconds to ensure RFID read has time to complete before considering updates
-    if (amsUpdateChanged && elapsed > 8000) {
-      console.log(`[useAmsOperations] Refresh complete: new AMS update after ${elapsed}ms for AMS ${amsId} tray ${trayId}`);
-      reset();
-    }
-  }, [state, context, amsUnits, lastAmsUpdate, reset]);
-
-  // Detect LOAD/UNLOAD completion via ams_status_main transition 1 → 0
-  useEffect(() => {
-    if (state !== 'LOADING' && state !== 'UNLOADING') {
-      prevAmsStatusMainRef.current = amsStatusMain;
-      return;
-    }
-
-    const wasActive = prevAmsStatusMainRef.current === 1;
-    const isNowIdle = amsStatusMain === 0;
-
-    if (wasActive && isNowIdle) {
-      console.log(`[useAmsOperations] ${state} complete: ams_status_main transitioned 1→0`);
-      reset();
-    }
-
-    prevAmsStatusMainRef.current = amsStatusMain;
-  }, [state, amsStatusMain, reset]);
-
-  // Secondary completion detection for LOAD: tray_now matches target
-  // Wait at least 5 seconds to ensure the filament actually reached the nozzle
-  // (tray_now can be updated before the physical load is complete)
-  useEffect(() => {
-    if (state !== 'LOADING' || !context?.loadTargetTrayId) return;
-
-    const elapsed = context?.startTime ? Date.now() - context.startTime : 0;
-    if (trayNow === context.loadTargetTrayId && elapsed > 5000) {
-      console.log(`[useAmsOperations] Load complete: tray_now=${trayNow} matches target (${elapsed}ms elapsed)`);
-      reset();
-    }
-  }, [state, context, trayNow, reset]);
-
-  // Secondary completion detection for UNLOAD: tray_now becomes 255
-  useEffect(() => {
-    if (state !== 'UNLOADING') return;
-
-    // Only trigger if we're past the initial phase (give it 1s to start)
-    const elapsed = context?.startTime ? Date.now() - context.startTime : 0;
-    if (trayNow === 255 && elapsed > 1000) {
-      console.log('[useAmsOperations] Unload complete: tray_now=255');
-      reset();
-    }
-  }, [state, context, trayNow, reset]);
-
-  // Cleanup on unmount
-  useEffect(() => {
-    return () => {
-      clearOperationTimeout();
-    };
-  }, [clearOperationTimeout]);
-
-  // === Derived State ===
-
-  const isOperationInProgress = state !== 'IDLE';
-
-  const isRefreshingSlot = useCallback((amsId: number, trayId: number) => {
-    if (state !== 'REFRESHING' || !context?.refreshTarget) return false;
-    return context.refreshTarget.amsId === amsId && context.refreshTarget.trayId === trayId;
-  }, [state, context]);
-
-  const isLoadOperation = state === 'LOADING';
-  const loadTargetTrayId = context?.loadTargetTrayId ?? null;
-
-  return {
-    state,
-    context,
-    startRefresh,
-    startLoad,
-    startUnload,
-    reset,
-    isOperationInProgress,
-    isRefreshingSlot,
-    isLoadOperation,
-    loadTargetTrayId,
-    lastOperationType,
-    loadError: loadMutation.error,
-    unloadError: unloadMutation.error,
-    refreshError: refreshMutation.error,
-  };
-}

+ 0 - 1
frontend/src/i18n/locales/de.ts

@@ -2,7 +2,6 @@ export default {
   // Navigation
   nav: {
     printers: 'Drucker',
-    control: 'Steuerung',
     archives: 'Archiv',
     queue: 'Warteschlange',
     stats: 'Statistiken',

+ 0 - 1
frontend/src/i18n/locales/en.ts

@@ -2,7 +2,6 @@ export default {
   // Navigation
   nav: {
     printers: 'Printers',
-    control: 'Control',
     archives: 'Archives',
     queue: 'Queue',
     stats: 'Statistics',

+ 0 - 294
frontend/src/pages/ControlPage.tsx

@@ -1,294 +0,0 @@
-import { useState, useEffect } from 'react';
-import { useQuery } from '@tanstack/react-query';
-import { useSearchParams } from 'react-router-dom';
-import { api } from '../api/client';
-import type { PrinterStatus } from '../api/client';
-import { CameraFeed } from '../components/control/CameraFeed';
-import { PrintStatus } from '../components/control/PrintStatus';
-import { TemperatureColumn } from '../components/control/TemperatureColumn';
-import { JogPad } from '../components/control/JogPad';
-import { BedControls } from '../components/control/BedControls';
-import { ExtruderControls } from '../components/control/ExtruderControls';
-import { AMSSectionDual } from '../components/control/AMSSectionDual';
-import { PrinterPartsModal } from '../components/control/PrinterPartsModal';
-import { PrintOptionsModal } from '../components/control/PrintOptionsModal';
-import { CalibrationModal } from '../components/control/CalibrationModal';
-import { Loader2, WifiOff, Video, Webcam } from 'lucide-react';
-import { WifiSignal } from '../components/icons/WifiSignal';
-
-export function ControlPage() {
-  const [searchParams, setSearchParams] = useSearchParams();
-  const [selectedPrinterId, setSelectedPrinterId] = useState<number | null>(null);
-  const [showPrinterParts, setShowPrinterParts] = useState(false);
-  const [showPrintOptions, setShowPrintOptions] = useState(false);
-  const [showCalibration, setShowCalibration] = useState(false);
-
-  // Fetch all printers
-  const { data: printers, isLoading: loadingPrinters } = useQuery({
-    queryKey: ['printers'],
-    queryFn: api.getPrinters,
-  });
-
-  // Get statuses for all printers
-  const { data: statuses } = useQuery({
-    queryKey: ['printerStatuses'],
-    queryFn: async () => {
-      if (!printers) return {};
-      const statusMap: Record<number, PrinterStatus> = {};
-      await Promise.all(
-        printers.map(async (p) => {
-          try {
-            statusMap[p.id] = await api.getPrinterStatus(p.id);
-          } catch {
-            // Printer offline
-          }
-        })
-      );
-      return statusMap;
-    },
-    enabled: !!printers && printers.length > 0,
-    refetchInterval: 2000,
-  });
-
-  // Initialize selected printer from URL or first printer
-  useEffect(() => {
-    const printerParam = searchParams.get('printer');
-    if (printerParam) {
-      const id = parseInt(printerParam, 10);
-      if (!isNaN(id)) {
-        setSelectedPrinterId(id);
-        return;
-      }
-    }
-    // Default to first printer
-    if (printers && printers.length > 0 && !selectedPrinterId) {
-      setSelectedPrinterId(printers[0].id);
-    }
-  }, [printers, searchParams, selectedPrinterId]);
-
-  // Update URL when printer changes
-  const handlePrinterSelect = (printerId: number) => {
-    setSelectedPrinterId(printerId);
-    setSearchParams({ printer: String(printerId) });
-  };
-
-  const selectedPrinter = printers?.find((p) => p.id === selectedPrinterId);
-  const selectedStatus = selectedPrinterId ? statuses?.[selectedPrinterId] : null;
-
-  // Calibration stages that indicate active calibration
-  const CALIBRATION_STAGES = new Set([1, 3, 13, 25, 39, 40, 47, 48, 50]);
-  const isCalibrating = selectedStatus
-    ? CALIBRATION_STAGES.has(selectedStatus.stg_cur)
-    : false;
-
-  if (loadingPrinters) {
-    return (
-      <div className="flex items-center justify-center h-screen">
-        <Loader2 className="w-8 h-8 animate-spin text-bambu-green" />
-      </div>
-    );
-  }
-
-  if (!printers || printers.length === 0) {
-    return (
-      <div className="flex flex-col items-center justify-center h-screen text-bambu-gray">
-        <WifiOff className="w-16 h-16 mb-4" />
-        <p className="text-xl">No printers configured</p>
-        <p className="text-sm mt-2">Add a printer in the Printers page first</p>
-      </div>
-    );
-  }
-
-  return (
-    <div className="h-screen flex flex-col bg-bambu-dark">
-      {/* Printer Tabs */}
-      <div className="bg-bambu-dark-secondary border-b border-bambu-dark-tertiary">
-        <div className="flex overflow-x-auto">
-          {printers.filter((p) => statuses?.[p.id]?.connected).map((printer) => {
-            const status = statuses?.[printer.id];
-            const isSelected = printer.id === selectedPrinterId;
-
-            return (
-              <button
-                key={printer.id}
-                onClick={() => handlePrinterSelect(printer.id)}
-                className={`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors whitespace-nowrap border-b-2 ${
-                  isSelected
-                    ? 'border-bambu-green text-bambu-green bg-bambu-dark'
-                    : 'border-transparent text-bambu-gray hover:text-white hover:bg-bambu-dark-tertiary'
-                }`}
-              >
-                <span className="w-2 h-2 rounded-full bg-bambu-green" />
-                {printer.name}
-                {status?.state && status.state !== 'IDLE' && (
-                  <span className="text-xs px-2 py-0.5 rounded bg-bambu-dark-tertiary">
-                    {status.state}
-                  </span>
-                )}
-              </button>
-            );
-          })}
-        </div>
-      </div>
-
-      {/* Main Content - Bambu Studio Layout */}
-      {selectedPrinter && (
-        <div className="flex-1 flex overflow-hidden">
-          {/* Left Panel - Camera & Print Progress */}
-          <div className="flex-1 flex flex-col bg-bambu-dark">
-            {/* Camera Header Icons - same height as Control header */}
-            <div className="flex items-center justify-end gap-2 px-3 py-2.5 bg-bambu-dark-secondary border-b border-bambu-dark-tertiary min-h-[44px]">
-              <button className={`p-1.5 rounded hover:bg-bambu-dark-tertiary ${selectedStatus?.sdcard ? 'text-bambu-green' : 'text-bambu-gray hover:text-white'}`}>
-                <img src="/icons/micro-sd.svg" alt="SD Card" className={`w-4 h-4 ${selectedStatus?.sdcard ? 'icon-green' : 'icon-theme'}`} />
-              </button>
-              <button className={`p-1.5 rounded hover:bg-bambu-dark-tertiary ${selectedStatus?.timelapse ? 'text-red-500' : 'text-bambu-gray hover:text-white'}`}>
-                <Video className="w-4 h-4" />
-              </button>
-              <button className={`p-1.5 rounded hover:bg-bambu-dark-tertiary ${selectedStatus?.ipcam ? 'text-bambu-green' : 'text-bambu-gray hover:text-white'}`}>
-                <Webcam className="w-4 h-4" />
-              </button>
-              <div
-                className="p-1.5 rounded"
-                title={selectedStatus?.wifi_signal != null ? `WiFi: ${selectedStatus.wifi_signal} dBm` : 'WiFi signal unknown'}
-              >
-                <WifiSignal signal={selectedStatus?.wifi_signal} className="w-4 h-4" />
-              </div>
-            </div>
-
-            {/* Camera Feed - Embedded directly */}
-            <div className="flex-1 bg-black">
-              <CameraFeed
-                printerId={selectedPrinter.id}
-                isConnected={selectedStatus?.connected ?? false}
-              />
-            </div>
-
-            {/* Status Bar */}
-            <div className="h-1 bg-bambu-green" />
-
-            {/* Print Progress with integrated controls */}
-            <div className="bg-bambu-dark-secondary p-4 px-5">
-              <PrintStatus
-                printerId={selectedPrinter.id}
-                status={selectedStatus}
-              />
-            </div>
-          </div>
-
-          {/* Right Panel - Control */}
-          <div className="w-[680px] flex flex-col bg-bambu-dark-secondary border-l border-bambu-dark-tertiary overflow-y-auto">
-            {/* Control Header - same height as Camera header */}
-            <div className="flex items-center justify-between px-3 py-2.5 border-b border-bambu-dark-tertiary min-h-[44px]">
-              <span className="text-sm text-bambu-gray">Control</span>
-              <div className="flex gap-2">
-                <button
-                  onClick={() => setShowPrinterParts(true)}
-                  className="px-4 py-1.5 text-xs rounded bg-bambu-green text-white hover:bg-bambu-green-dark"
-                >
-                  Printer Parts
-                </button>
-                <button
-                  onClick={() => setShowPrintOptions(true)}
-                  className="px-4 py-1.5 text-xs rounded bg-bambu-green text-white hover:bg-bambu-green-dark"
-                >
-                  Print Options
-                </button>
-                <button
-                  onClick={() => setShowCalibration(true)}
-                  className="px-4 py-1.5 text-xs rounded bg-bambu-green text-white hover:bg-bambu-green-dark"
-                >
-                  Calibration
-                </button>
-              </div>
-            </div>
-
-            {/* Connection Warning */}
-            {!selectedStatus?.connected && (
-              <div className="m-3 p-3 bg-red-500/20 border border-red-500/50 rounded-lg flex items-center gap-3">
-                <WifiOff className="w-4 h-4 text-red-500" />
-                <span className="text-sm text-red-400">
-                  Printer is not connected. Controls are disabled.
-                </span>
-              </div>
-            )}
-
-            {/* Control Body */}
-            <div className="flex-1 p-4 bg-bambu-dark">
-              {/* Top Section: Temp + Movement + Extruder */}
-              <div className="mb-4 bg-bambu-dark-tertiary rounded-[10px] p-3">
-                <div className="flex gap-4 bg-bambu-dark-secondary rounded-[8px] p-4 overflow-hidden" style={{ minHeight: '300px' }}>
-                  {/* Temperature Column */}
-                  <TemperatureColumn
-                    printer={selectedPrinter}
-                    status={selectedStatus}
-                    disabled={isCalibrating}
-                  />
-
-                {/* Movement Column */}
-                <div className="flex-1 flex gap-6 items-center justify-center">
-                  {/* Jog Section */}
-                  <div className="flex flex-col items-center">
-                    <JogPad
-                      printerId={selectedPrinter.id}
-                      status={selectedStatus}
-                      disabled={isCalibrating}
-                    />
-                    <BedControls
-                      printerId={selectedPrinter.id}
-                      status={selectedStatus}
-                      disabled={isCalibrating}
-                    />
-                  </div>
-
-                  {/* Extruder Section */}
-                  <ExtruderControls
-                    printerId={selectedPrinter.id}
-                    status={selectedStatus}
-                    nozzleCount={selectedPrinter.nozzle_count}
-                    disabled={isCalibrating}
-                  />
-                </div>
-                </div>
-              </div>
-
-              {/* AMS Section */}
-              <AMSSectionDual
-                printerId={selectedPrinter.id}
-                printerModel={selectedPrinter.model || 'X1C'}
-                status={selectedStatus}
-                nozzleCount={selectedPrinter.nozzle_count}
-              />
-            </div>
-          </div>
-        </div>
-      )}
-
-      {/* Printer Parts Modal */}
-      {showPrinterParts && selectedPrinter && (
-        <PrinterPartsModal
-          printer={selectedPrinter}
-          status={selectedStatus}
-          onClose={() => setShowPrinterParts(false)}
-        />
-      )}
-
-      {/* Print Options Modal */}
-      {showPrintOptions && selectedPrinter && (
-        <PrintOptionsModal
-          printer={selectedPrinter}
-          status={selectedStatus}
-          onClose={() => setShowPrintOptions(false)}
-        />
-      )}
-
-      {/* Calibration Modal */}
-      {showCalibration && selectedPrinter && (
-        <CalibrationModal
-          printer={selectedPrinter}
-          status={selectedStatus}
-          onClose={() => setShowCalibration(false)}
-        />
-      )}
-    </div>
-  );
-}

+ 0 - 1026
mockup/control-page-mockup.html

@@ -1,1026 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control Page Mockup</title>
-  <style>
-    * {
-      margin: 0;
-      padding: 0;
-      box-sizing: border-box;
-    }
-
-    :root {
-      --bg-primary: #1a1a1a;
-      --bg-secondary: #2d2d2d;
-      --bg-tertiary: #3d3d3d;
-      --text-primary: #ffffff;
-      --text-secondary: #888888;
-      --accent: #00ae42;
-      --accent-dark: #008c35;
-      --orange: #f5a623;
-      --red: #e74c3c;
-      --blue: #3498db;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-primary);
-      color: var(--text-primary);
-      min-height: 100vh;
-    }
-
-    /* Printer Tabs */
-    .printer-tabs {
-      display: flex;
-      background: var(--bg-secondary);
-      border-bottom: 1px solid var(--bg-tertiary);
-      padding: 0 16px;
-    }
-
-    .printer-tab {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 12px 20px;
-      border: none;
-      background: none;
-      color: var(--text-secondary);
-      cursor: pointer;
-      border-bottom: 2px solid transparent;
-      font-size: 14px;
-    }
-
-    .printer-tab.active {
-      color: var(--accent);
-      border-bottom-color: var(--accent);
-      background: var(--bg-primary);
-    }
-
-    .printer-tab .status-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      background: var(--accent);
-    }
-
-    .printer-tab .status-dot.offline {
-      background: var(--red);
-    }
-
-    /* Main Layout */
-    .main-content {
-      display: grid;
-      grid-template-columns: 1fr 380px;
-      height: calc(100vh - 49px);
-      gap: 0;
-    }
-
-    /* Left Column */
-    .left-column {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-primary);
-    }
-
-    /* Camera Section */
-    .camera-section {
-      flex: 1;
-      background: #000;
-      position: relative;
-      min-height: 0;
-    }
-
-    .camera-feed {
-      width: 100%;
-      height: 100%;
-      object-fit: contain;
-      background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 12px;
-      left: 12px;
-      display: flex;
-      gap: 8px;
-    }
-
-    .camera-btn {
-      padding: 6px 12px;
-      background: rgba(0,0,0,0.6);
-      border: none;
-      border-radius: 4px;
-      color: white;
-      cursor: pointer;
-      font-size: 12px;
-    }
-
-    .camera-btn:hover {
-      background: rgba(0,0,0,0.8);
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-secondary);
-      padding: 16px;
-      display: flex;
-      gap: 16px;
-      align-items: center;
-    }
-
-    .print-thumbnail {
-      width: 80px;
-      height: 80px;
-      background: var(--bg-tertiary);
-      border-radius: 8px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-secondary);
-      font-size: 12px;
-    }
-
-    .print-info {
-      flex: 1;
-    }
-
-    .print-name {
-      font-weight: 500;
-      margin-bottom: 8px;
-      color: var(--text-primary);
-    }
-
-    .progress-bar {
-      height: 6px;
-      background: var(--bg-tertiary);
-      border-radius: 3px;
-      margin-bottom: 8px;
-      overflow: hidden;
-    }
-
-    .progress-fill {
-      height: 100%;
-      background: var(--accent);
-      width: 45%;
-      transition: width 0.3s;
-    }
-
-    .print-stats {
-      display: flex;
-      gap: 24px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .print-stats span {
-      color: var(--text-primary);
-    }
-
-    /* Print Controls - in progress section */
-    .print-controls-inline {
-      display: flex;
-      gap: 8px;
-      margin-left: 16px;
-    }
-
-    .print-btn-small {
-      width: 40px;
-      height: 40px;
-      border: none;
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 16px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .print-btn-small.pause {
-      background: #f39c12;
-      color: white;
-    }
-
-    .print-btn-small.stop {
-      background: var(--red);
-      color: white;
-    }
-
-    .print-btn-small:hover {
-      opacity: 0.9;
-    }
-
-    .print-btn-small:disabled {
-      opacity: 0.4;
-      cursor: not-allowed;
-    }
-
-    /* Right Column - Control Panel */
-    .control-panel {
-      background: var(--bg-secondary);
-      overflow-y: auto;
-      display: flex;
-      flex-direction: column;
-    }
-
-    /* Section */
-    .section {
-      padding: 16px;
-      border-bottom: 1px solid var(--bg-tertiary);
-    }
-
-    .section-title {
-      font-size: 12px;
-      color: var(--text-secondary);
-      margin-bottom: 12px;
-      text-transform: uppercase;
-      letter-spacing: 0.5px;
-    }
-
-    /* Temperatures */
-    .temps-grid {
-      display: grid;
-      grid-template-columns: 1fr 1fr;
-      gap: 8px;
-    }
-
-    .temp-item {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 8px 12px;
-      background: var(--bg-primary);
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .temp-item:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .temp-icon {
-      width: 24px;
-      height: 24px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 14px;
-    }
-
-    .temp-values {
-      display: flex;
-      align-items: baseline;
-      gap: 4px;
-    }
-
-    .temp-current {
-      font-size: 18px;
-      font-weight: 500;
-    }
-
-    .temp-current.hot {
-      color: var(--orange);
-    }
-
-    .temp-target {
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    /* Quick Controls Row */
-    .quick-controls {
-      display: flex;
-      gap: 8px;
-      margin-top: 12px;
-    }
-
-    .quick-btn {
-      flex: 1;
-      padding: 8px;
-      background: var(--bg-primary);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 12px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 6px;
-    }
-
-    .quick-btn:hover {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    .quick-btn.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    /* Speed Control */
-    .speed-control {
-      display: flex;
-      gap: 4px;
-    }
-
-    .speed-btn {
-      flex: 1;
-      padding: 10px 8px;
-      background: var(--bg-primary);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-      text-align: center;
-    }
-
-    .speed-btn:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .speed-btn.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    .speed-btn.ludicrous {
-      color: var(--red);
-    }
-
-    .speed-btn.ludicrous.active {
-      background: var(--red);
-      color: white;
-    }
-
-    /* Movement Controls */
-    .movement-section {
-      display: flex;
-      gap: 16px;
-      align-items: flex-start;
-    }
-
-    .jog-pad {
-      position: relative;
-      width: 140px;
-      height: 140px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: var(--bg-primary);
-      position: relative;
-    }
-
-    .jog-btn {
-      position: absolute;
-      width: 36px;
-      height: 36px;
-      background: var(--bg-tertiary);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-primary);
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 16px;
-    }
-
-    .jog-btn:hover {
-      background: var(--accent);
-    }
-
-    .jog-btn.up { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 44px;
-      height: 44px;
-      background: var(--accent);
-      border: none;
-      border-radius: 50%;
-      color: white;
-      cursor: pointer;
-      font-size: 18px;
-    }
-
-    .jog-home:hover {
-      background: var(--accent-dark);
-    }
-
-    /* Z Controls */
-    .z-controls {
-      display: flex;
-      flex-direction: column;
-      gap: 8px;
-      align-items: center;
-    }
-
-    .z-btn {
-      width: 44px;
-      height: 44px;
-      background: var(--bg-primary);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-primary);
-      cursor: pointer;
-      font-size: 16px;
-    }
-
-    .z-btn:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .z-label {
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    /* Step Size */
-    .step-sizes {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-    }
-
-    .step-btn {
-      padding: 6px 12px;
-      background: var(--bg-primary);
-      border: none;
-      border-radius: 4px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-    }
-
-    .step-btn:hover, .step-btn.active {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    /* Extruder */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-toggle {
-      display: flex;
-      background: var(--bg-primary);
-      border-radius: 6px;
-      overflow: hidden;
-    }
-
-    .extruder-toggle button {
-      padding: 6px 16px;
-      border: none;
-      background: none;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 12px;
-    }
-
-    .extruder-toggle button.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    .extruder-graphic {
-      width: 60px;
-      height: 80px;
-      background: var(--bg-primary);
-      border-radius: 8px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-secondary);
-      font-size: 10px;
-    }
-
-    .extruder-btns {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-    }
-
-    /* AMS Section */
-    .ams-section {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    /* AMS Tabs - like Bambu Studio color indicators */
-    .ams-tabs {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 12px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      gap: 2px;
-      padding: 6px 10px;
-      background: var(--bg-primary);
-      border: 2px solid transparent;
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .ams-tab.active {
-      border-color: var(--accent);
-    }
-
-    .ams-tab-colors {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab-dot {
-      width: 12px;
-      height: 12px;
-      border-radius: 2px;
-    }
-
-    .ams-tab-label {
-      font-size: 10px;
-      color: var(--text-secondary);
-      margin-left: 4px;
-    }
-
-    /* AMS Unit Content */
-    .ams-unit-content {
-      background: var(--bg-primary);
-      border-radius: 8px;
-      padding: 12px;
-    }
-
-    .ams-unit-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 10px;
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    .ams-slots {
-      display: flex;
-      gap: 8px;
-      justify-content: center;
-    }
-
-    .ams-slot {
-      width: 56px;
-      background: var(--bg-secondary);
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid transparent;
-      transition: all 0.2s;
-    }
-
-    .ams-slot:hover {
-      border-color: var(--text-secondary);
-    }
-
-    .ams-slot.selected {
-      border-color: var(--accent);
-      box-shadow: 0 0 0 2px rgba(0, 174, 66, 0.3);
-    }
-
-    .ams-slot.empty {
-      opacity: 0.4;
-    }
-
-    .ams-slot-color {
-      height: 36px;
-      position: relative;
-    }
-
-    .ams-slot-color::after {
-      content: '';
-      position: absolute;
-      bottom: 0;
-      left: 0;
-      right: 0;
-      height: 8px;
-      background: linear-gradient(transparent, rgba(0,0,0,0.3));
-    }
-
-    .ams-slot-info {
-      padding: 6px 4px;
-      text-align: center;
-      background: var(--bg-tertiary);
-    }
-
-    .ams-slot-type {
-      font-size: 10px;
-      font-weight: 600;
-      color: var(--text-primary);
-    }
-
-    .ams-slot-num {
-      font-size: 9px;
-      color: var(--text-secondary);
-      margin-top: 2px;
-    }
-
-    /* External Spool */
-    .external-spool {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 12px;
-    }
-
-    .ext-label {
-      font-size: 11px;
-      color: var(--text-secondary);
-      width: 30px;
-    }
-
-    .ext-slot {
-      width: 56px;
-      height: 64px;
-      background: var(--bg-primary);
-      border-radius: 8px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-secondary);
-      font-size: 18px;
-      border: 2px solid transparent;
-      cursor: pointer;
-    }
-
-    .ext-slot:hover {
-      border-color: var(--text-secondary);
-    }
-
-    .ext-slot-label {
-      font-size: 9px;
-      margin-top: 4px;
-    }
-
-    .ext-graphic {
-      flex: 1;
-      height: 64px;
-      background: var(--bg-primary);
-      border-radius: 8px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      gap: 8px;
-      margin-top: 12px;
-    }
-
-    .ams-btn {
-      flex: 1;
-      padding: 10px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 13px;
-      font-weight: 500;
-    }
-
-    .ams-btn.secondary {
-      background: var(--bg-primary);
-      color: var(--text-primary);
-    }
-
-    .ams-btn.secondary:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .ams-btn.primary {
-      background: var(--accent);
-      color: white;
-    }
-
-    .ams-btn.primary:hover {
-      background: var(--accent-dark);
-    }
-
-    /* AMS Animation Area - reserved space for filament path animation */
-    .ams-animation-area {
-      flex: 1;
-      min-height: 100px;
-      margin-top: 16px;
-      background: var(--bg-primary);
-      border-radius: 8px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      position: relative;
-    }
-
-    .ams-hub-graphic {
-      width: 80px;
-      height: 24px;
-      background: var(--bg-tertiary);
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 10px;
-      color: var(--text-secondary);
-    }
-
-    .ams-filament-path {
-      position: absolute;
-      top: 0;
-      left: 50%;
-      width: 2px;
-      height: 30px;
-      background: var(--accent);
-      transform: translateX(-50%);
-    }
-
-    .ams-status-text {
-      font-size: 11px;
-      color: var(--text-secondary);
-      margin-top: 12px;
-    }
-  </style>
-</head>
-<body>
-  <!-- Printer Tabs -->
-  <div class="printer-tabs">
-    <button class="printer-tab active">
-      <span class="status-dot"></span>
-      H2D-1
-      <span style="font-size: 11px; padding: 2px 8px; background: var(--bg-tertiary); border-radius: 4px;">IDLE</span>
-    </button>
-    <button class="printer-tab">
-      <span class="status-dot"></span>
-      X1C-2
-    </button>
-    <button class="printer-tab">
-      <span class="status-dot offline"></span>
-      P1S-3
-    </button>
-  </div>
-
-  <!-- Main Content -->
-  <div class="main-content">
-    <!-- Left Column: Camera + Progress -->
-    <div class="left-column">
-      <div class="camera-section">
-        <div class="camera-feed">
-          <!-- Camera placeholder -->
-          <div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #666;">
-            📷 Camera Feed
-          </div>
-        </div>
-        <div class="camera-overlay">
-          <button class="camera-btn">▶ Start</button>
-          <button class="camera-btn">⛶ Fullscreen</button>
-        </div>
-      </div>
-
-      <div class="print-progress">
-        <div class="print-thumbnail">
-          No Print
-        </div>
-        <div class="print-info">
-          <div class="print-name">Idle</div>
-          <div class="progress-bar">
-            <div class="progress-fill" style="width: 0%"></div>
-          </div>
-          <div class="print-stats">
-            Layer: <span>--/--</span>
-            Time: <span>--:--</span>
-            Remaining: <span>--:--</span>
-          </div>
-        </div>
-        <div class="print-controls-inline">
-          <button class="print-btn-small pause" disabled>⏸</button>
-          <button class="print-btn-small stop" disabled>⏹</button>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Column: Control Panel -->
-    <div class="control-panel">
-      <!-- Temperatures -->
-      <div class="section">
-        <div class="section-title">Temperature</div>
-        <div class="temps-grid">
-          <div class="temp-item">
-            <div class="temp-icon">🔥L</div>
-            <div class="temp-values">
-              <span class="temp-current">22</span>
-              <span class="temp-target">/0°C</span>
-            </div>
-          </div>
-          <div class="temp-item">
-            <div class="temp-icon">🔥R</div>
-            <div class="temp-values">
-              <span class="temp-current">21</span>
-              <span class="temp-target">/0°C</span>
-            </div>
-          </div>
-          <div class="temp-item">
-            <div class="temp-icon">🛏️</div>
-            <div class="temp-values">
-              <span class="temp-current">21</span>
-              <span class="temp-target">/0°C</span>
-            </div>
-          </div>
-          <div class="temp-item">
-            <div class="temp-icon">📦</div>
-            <div class="temp-values">
-              <span class="temp-current">21</span>
-              <span class="temp-target">/0°C</span>
-            </div>
-          </div>
-        </div>
-        <div class="quick-controls">
-          <button class="quick-btn">🌀 Fans</button>
-          <button class="quick-btn active">💡 Lamp</button>
-          <button class="quick-btn">❄️ Cool</button>
-        </div>
-      </div>
-
-      <!-- Speed -->
-      <div class="section">
-        <div class="section-title">Print Speed</div>
-        <div class="speed-control">
-          <button class="speed-btn">Silent</button>
-          <button class="speed-btn active">Standard</button>
-          <button class="speed-btn">Sport</button>
-          <button class="speed-btn ludicrous">Ludicrous</button>
-        </div>
-      </div>
-
-      <!-- Movement -->
-      <div class="section">
-        <div class="section-title">Movement</div>
-        <div class="movement-section">
-          <div class="jog-pad">
-            <div class="jog-ring">
-              <button class="jog-btn up">▲</button>
-              <button class="jog-btn down">▼</button>
-              <button class="jog-btn left">◀</button>
-              <button class="jog-btn right">▶</button>
-              <button class="jog-home">⌂</button>
-            </div>
-          </div>
-
-          <div class="z-controls">
-            <button class="z-btn">▲</button>
-            <span class="z-label">Z</span>
-            <button class="z-btn">▼</button>
-          </div>
-
-          <div class="step-sizes">
-            <button class="step-btn">10mm</button>
-            <button class="step-btn active">1mm</button>
-            <button class="step-btn">0.1mm</button>
-          </div>
-
-          <div class="extruder-section">
-            <div class="extruder-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic">
-              🖨️<br>Extruder
-            </div>
-            <div class="extruder-btns">
-              <button class="z-btn" style="width: 36px; height: 28px; font-size: 12px;">▲</button>
-              <button class="z-btn" style="width: 36px; height: 28px; font-size: 12px;">▼</button>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <!-- AMS -->
-      <div class="section ams-section">
-        <div class="section-title">AMS</div>
-
-        <!-- AMS Tabs - color indicators like Bambu Studio -->
-        <div class="ams-tabs">
-          <div class="ams-tab active">
-            <div class="ams-tab-colors">
-              <div class="ams-tab-dot" style="background: #FFD700;"></div>
-              <div class="ams-tab-dot" style="background: #1a1a1a;"></div>
-              <div class="ams-tab-dot" style="background: #8B4513;"></div>
-              <div class="ams-tab-dot" style="background: #2d2d2d;"></div>
-            </div>
-            <span class="ams-tab-label">1</span>
-          </div>
-          <div class="ams-tab">
-            <div class="ams-tab-colors">
-              <div class="ams-tab-dot" style="background: #FF0000;"></div>
-              <div class="ams-tab-dot" style="background: #0066FF;"></div>
-              <div class="ams-tab-dot" style="background: #3d3d3d;"></div>
-              <div class="ams-tab-dot" style="background: #3d3d3d;"></div>
-            </div>
-            <span class="ams-tab-label">2</span>
-          </div>
-          <div class="ams-tab">
-            <div class="ams-tab-colors">
-              <div class="ams-tab-dot" style="background: #00FF00;"></div>
-              <div class="ams-tab-dot" style="background: #FFFFFF;"></div>
-              <div class="ams-tab-dot" style="background: #FF69B4;"></div>
-              <div class="ams-tab-dot" style="background: #9932CC;"></div>
-            </div>
-            <span class="ams-tab-label">3</span>
-          </div>
-        </div>
-
-        <!-- AMS Unit Content (shows selected AMS) -->
-        <div class="ams-unit-content">
-          <div class="ams-unit-header">
-            <span>AMS 1</span>
-            <span>💧 18% · 🌡️ 24°C</span>
-          </div>
-          <div class="ams-slots">
-            <div class="ams-slot selected">
-              <div class="ams-slot-color" style="background: #FFD700;"></div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PLA</div>
-                <div class="ams-slot-num">A1</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: #1a1a1a;"></div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PETG</div>
-                <div class="ams-slot-num">A2</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: #8B4513;"></div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PETG</div>
-                <div class="ams-slot-num">A3</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: #2d2d2d;"></div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PLA</div>
-                <div class="ams-slot-num">A4</div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- External Spool -->
-        <div class="external-spool">
-          <span class="ext-label">Ext</span>
-          <div class="ext-slot">
-            ?
-            <span class="ext-slot-label">Spool</span>
-          </div>
-          <div class="ext-graphic">🎞️ External Spool Holder</div>
-        </div>
-
-        <!-- AMS Actions -->
-        <div class="ams-actions">
-          <button class="ams-btn secondary">Unload</button>
-          <button class="ams-btn primary">Load</button>
-        </div>
-
-        <!-- AMS Animation Area - reserved for filament path animation -->
-        <div class="ams-animation-area">
-          <div class="ams-hub-graphic">HUB</div>
-          <div class="ams-status-text">Ready - Select a slot to load</div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 923
mockup/control-page-v10.html

@@ -1,923 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v10</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f5f5f5;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 450px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 14px; align-items: center; }
-    .progress-thumb {
-      width: 60px; height: 60px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 24px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column { flex: 0 0 auto; }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 20px;
-      text-align: center;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-value {
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 20px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 110px;
-      height: 110px;
-      margin-bottom: 10px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 22px;
-      height: 22px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 18px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 18px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 18px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 18px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 18px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .bed-btn {
-      padding: 7px 12px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 6px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .extruder-btn {
-      width: 32px;
-      height: 32px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 70px;
-      margin: 6px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== FILAMENT BOXES ========== */
-    .filament-boxes {
-      display: flex;
-      gap: 10px;
-      margin-bottom: 14px;
-      padding-bottom: 14px;
-      border-bottom: 1px solid var(--border-light);
-      flex-wrap: wrap;
-    }
-
-    .filament-box {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      padding: 10px 12px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-    }
-
-    .filament-box:hover { border-color: #ccc; }
-    .filament-box.active { border-color: var(--accent); }
-
-    .filament-dots { display: flex; gap: 4px; }
-
-    .f-dot {
-      width: 18px;
-      height: 18px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .f-dot.empty {
-      background: #e8e8e8 !important;
-      border: 1px solid #ccc;
-    }
-
-    .f-dot-sep {
-      width: 1px;
-      height: 18px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .filament-count {
-      font-size: 13px;
-      color: var(--text-muted);
-      margin-left: 6px;
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      display: flex;
-      gap: 14px;
-    }
-
-    .ams-unit {
-      flex: 1;
-      min-width: 0;
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 14px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 12px;
-    }
-
-    .ams-humidity {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 14px;
-      color: var(--text-secondary);
-    }
-
-    .ams-humidity img { width: 16px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 8px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 11px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 80px;
-      border-radius: 10px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 8px;
-    }
-
-    .ams-slot-type {
-      font-size: 12px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 4px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 18px; height: 18px; }
-    .ams-slot-eye img { width: 16px; height: 16px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Connector Lines */
-    .ams-connectors {
-      display: flex;
-      justify-content: center;
-      padding: 0 10px;
-    }
-
-    .connector-group {
-      flex: 1;
-      display: flex;
-      gap: 6px;
-      position: relative;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 14px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 12%;
-      right: 12%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-top: 14px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-      flex-wrap: wrap;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 9px 16px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 20px; opacity: 0.5; }
-
-    /* Hub */
-    .hub-section {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 4px;
-    }
-
-    .hub-line { width: 30px; height: 2px; background: #c0c0c0; }
-
-    .hub-graphic {
-      display: flex;
-      padding: 5px 8px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-    }
-
-    .hub-port {
-      width: 10px;
-      height: 14px;
-      background: var(--accent);
-      border-radius: 2px;
-      margin: 0 2px;
-    }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 9px 24px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div style="display: flex; align-items: center; gap: 16px; margin-top: 8px;">
-              <div class="air-value"><img src="icons/ventilation.svg" style="width:16px; opacity:0.5; vertical-align:middle;"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Filament Assignment Boxes -->
-        <div class="filament-boxes">
-          <div class="filament-box active">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FFD700;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot-sep"></div>
-              <div class="f-dot" style="background: #FF3366;"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-        </div>
-
-        <!-- AMS Section - Two units side by side -->
-        <div class="ams-section">
-          <!-- AMS Unit 1 -->
-          <div class="ams-unit">
-            <div class="ams-header">
-              <div class="ams-humidity"><img src="icons/water.svg"> 17 %</div>
-              <div class="ams-humidity"><span class="sun">&#9728;</span></div>
-            </div>
-            <div class="slot-labels">
-              <div class="slot-label">A1&#8595;</div>
-              <div class="slot-label">A2&#8595;</div>
-              <div class="slot-label">A3&#8595;</div>
-              <div class="slot-label">A4&#8595;</div>
-            </div>
-            <div class="ams-slots">
-              <div class="ams-slot active">
-                <div class="ams-slot-fill" style="background: #FFD700;">
-                  <span class="ams-slot-type dark">PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #333;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #8B4513;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Unit 2 -->
-          <div class="ams-unit">
-            <div class="ams-header">
-              <div class="ams-humidity"><img src="icons/water.svg"> 14 %</div>
-              <div class="ams-humidity"><span class="sun">&#9728;</span></div>
-            </div>
-            <div class="slot-labels">
-              <div class="slot-label">B1&#8595;</div>
-              <div class="slot-label">B2&#8595;</div>
-              <div class="slot-label">B3&#8595;</div>
-              <div class="slot-label">B4&#8595;</div>
-            </div>
-            <div class="ams-slots">
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #888;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #888;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #aaa;">
-                  <span class="ams-slot-type dark">Sup.PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Actions -->
-        <div class="ams-actions">
-          <button class="auto-refill-btn">Auto-refill</button>
-          <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-          <div class="hub-section">
-            <div class="hub-line"></div>
-            <div class="hub-graphic">
-              <div class="hub-port"></div>
-              <div class="hub-port"></div>
-            </div>
-            <div class="hub-line"></div>
-          </div>
-          <div class="ams-spacer"></div>
-          <button class="ams-action-btn">Unload</button>
-          <button class="ams-action-btn">Load</button>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 936
mockup/control-page-v11.html

@@ -1,936 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v11</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f5f5f5;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 580px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 14px; align-items: center; }
-    .progress-thumb {
-      width: 60px; height: 60px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 140px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 20px;
-      text-align: center;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== FILAMENT BOXES ========== */
-    .filament-boxes {
-      display: flex;
-      gap: 10px;
-      margin-bottom: 16px;
-      padding-bottom: 16px;
-      border-bottom: 1px solid var(--border-light);
-    }
-
-    .filament-box {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      padding: 10px 12px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-    }
-
-    .filament-box:hover { border-color: #ccc; }
-    .filament-box.active { border-color: var(--accent); }
-
-    .filament-dots { display: flex; gap: 4px; }
-
-    .f-dot {
-      width: 18px;
-      height: 18px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .f-dot.empty {
-      background: #e8e8e8 !important;
-      border: 1px solid #ccc;
-    }
-
-    .f-dot-sep {
-      width: 1px;
-      height: 18px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .filament-count {
-      font-size: 13px;
-      color: var(--text-muted);
-      margin-left: 6px;
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      display: flex;
-      gap: 16px;
-    }
-
-    .ams-unit {
-      flex: 1;
-      min-width: 0;
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 14px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 12px;
-    }
-
-    .ams-humidity {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 14px;
-      color: var(--text-secondary);
-    }
-
-    .ams-humidity img { width: 16px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 8px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 11px;
-      color: var(--text-muted);
-      padding: 4px 0;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 80px;
-      border-radius: 10px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 8px;
-    }
-
-    .ams-slot-type {
-      font-size: 12px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 4px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 18px; height: 18px; }
-    .ams-slot-eye img { width: 16px; height: 16px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Connector Lines */
-    .ams-connectors {
-      display: flex;
-      justify-content: center;
-      padding: 0 10px;
-    }
-
-    .connector-group {
-      flex: 1;
-      display: flex;
-      gap: 6px;
-      position: relative;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 14px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 12%;
-      right: 12%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 16px;
-      padding-top: 16px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    /* Hub */
-    .hub-section {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 4px;
-    }
-
-    .hub-line { width: 40px; height: 2px; background: #c0c0c0; }
-
-    .hub-graphic {
-      display: flex;
-      padding: 6px 10px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-    }
-
-    .hub-port {
-      width: 12px;
-      height: 16px;
-      background: var(--accent);
-      border-radius: 2px;
-      margin: 0 3px;
-    }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Filament Assignment Boxes -->
-        <div class="filament-boxes">
-          <div class="filament-box active">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FFD700;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot-sep"></div>
-              <div class="f-dot" style="background: #FF3366;"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-        </div>
-
-        <!-- AMS Section - Two units side by side -->
-        <div class="ams-section">
-          <!-- AMS Unit 1 -->
-          <div class="ams-unit">
-            <div class="ams-header">
-              <div class="ams-humidity"><img src="icons/water.svg"> 17 %</div>
-              <div class="ams-humidity"><span class="sun">&#9728;</span></div>
-            </div>
-            <div class="slot-labels">
-              <div class="slot-label">A1&#8595;</div>
-              <div class="slot-label">A2&#8595;</div>
-              <div class="slot-label">A3&#8595;</div>
-              <div class="slot-label">A4&#8595;</div>
-            </div>
-            <div class="ams-slots">
-              <div class="ams-slot active">
-                <div class="ams-slot-fill" style="background: #FFD700;">
-                  <span class="ams-slot-type dark">PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #333;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #8B4513;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Unit 2 -->
-          <div class="ams-unit">
-            <div class="ams-header">
-              <div class="ams-humidity"><img src="icons/water.svg"> 14 %</div>
-              <div class="ams-humidity"><span class="sun">&#9728;</span></div>
-            </div>
-            <div class="slot-labels">
-              <div class="slot-label">B1&#8595;</div>
-              <div class="slot-label">B2&#8595;</div>
-              <div class="slot-label">B3&#8595;</div>
-              <div class="slot-label">B4&#8595;</div>
-            </div>
-            <div class="ams-slots">
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #888;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #888;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #aaa;">
-                  <span class="ams-slot-type dark">Sup.PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Actions -->
-        <div class="ams-actions">
-          <button class="auto-refill-btn">Auto-refill</button>
-          <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-          <div class="hub-section">
-            <div class="hub-line"></div>
-            <div class="hub-graphic">
-              <div class="hub-port"></div>
-              <div class="hub-port"></div>
-            </div>
-            <div class="hub-line"></div>
-          </div>
-          <div class="ams-spacer"></div>
-          <button class="ams-action-btn">Unload</button>
-          <button class="ams-action-btn">Load</button>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1174
mockup/control-page-v12.html

@@ -1,1174 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v12</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f5f5f5;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 580px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-thumb img {
-      width: 100%;
-      height: 100%;
-      object-fit: cover;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== FILAMENT BOXES ========== */
-    .filament-boxes {
-      display: flex;
-      gap: 10px;
-      margin-bottom: 16px;
-      padding-bottom: 16px;
-      border-bottom: 1px solid var(--border-light);
-      flex-wrap: wrap;
-    }
-
-    .filament-box {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      padding: 10px 12px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-    }
-
-    .filament-box:hover { border-color: #ccc; }
-    .filament-box.active { border-color: var(--accent); }
-
-    .filament-dots { display: flex; gap: 4px; }
-
-    .f-dot {
-      width: 18px;
-      height: 18px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .f-dot.empty {
-      background: #e8e8e8 !important;
-      border: 1px solid #ccc;
-    }
-
-    .f-dot-sep {
-      width: 1px;
-      height: 18px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .filament-count {
-      font-size: 13px;
-      color: var(--text-muted);
-      margin-left: 6px;
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-container {
-      display: flex;
-      flex-direction: column;
-      gap: 12px;
-    }
-
-    .ams-row {
-      display: flex;
-      gap: 12px;
-    }
-
-    .ams-unit {
-      flex: 1;
-      min-width: 0;
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 12px;
-    }
-
-    .ams-unit.ams-ht {
-      flex: 0 0 auto;
-      width: 100px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-bottom: 10px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-    .ams-stat .temp-icon { font-size: 12px; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 60px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 6px;
-    }
-
-    .ams-slot-type {
-      font-size: 10px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 2px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 14px; height: 14px; }
-    .ams-slot-eye img { width: 12px; height: 12px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Connector Lines */
-    .ams-connectors {
-      display: flex;
-      justify-content: center;
-      padding: 0 8px;
-    }
-
-    .connector-group {
-      flex: 1;
-      display: flex;
-      gap: 4px;
-      position: relative;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 10px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 12%;
-      right: 12%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* AMS-HT single slot */
-    .ams-ht .ams-slots {
-      justify-content: center;
-    }
-
-    .ams-ht .ams-slot {
-      flex: none;
-      width: 50px;
-    }
-
-    .ams-ht .slot-labels {
-      justify-content: center;
-    }
-
-    .ams-ht .slot-label {
-      flex: none;
-      width: 50px;
-    }
-
-    .ams-ht .ams-connectors {
-      justify-content: center;
-    }
-
-    .ams-ht .connector-group {
-      flex: none;
-      width: 50px;
-    }
-
-    .ams-ht .connector-hbar {
-      display: none;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 14px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    /* Hub */
-    .hub-section {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 4px;
-    }
-
-    .hub-line { width: 40px; height: 2px; background: #c0c0c0; }
-
-    .hub-graphic {
-      display: flex;
-      padding: 6px 10px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-    }
-
-    .hub-port {
-      width: 12px;
-      height: 16px;
-      background: var(--accent);
-      border-radius: 2px;
-      margin: 0 3px;
-    }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Filament Assignment Boxes -->
-        <div class="filament-boxes">
-          <div class="filament-box active">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FFD700;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #8B4513;"></div>
-              <div class="f-dot" style="background: #666;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FF6600;"></div>
-              <div class="f-dot" style="background: #00AA00;"></div>
-              <div class="f-dot" style="background: #0066FF;"></div>
-              <div class="f-dot" style="background: #FF0000;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FFFFFF; border-color: #ccc;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #666;"></div>
-              <div class="f-dot" style="background: #999;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #CC99FF;"></div>
-              <div class="f-dot" style="background: #FFCC00;"></div>
-              <div class="f-dot" style="background: #00CCCC;"></div>
-              <div class="f-dot" style="background: #FF66CC;"></div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section - 4x AMS + 2x AMS-HT -->
-        <div class="ams-container">
-          <!-- Row 1: AMS 1 + AMS 2 -->
-          <div class="ams-row">
-            <div class="ams-unit">
-              <div class="ams-header">
-                <div class="ams-stat"><img src="icons/water.svg"> 17%</div>
-                <div class="ams-stat">25°C</div>
-                <div class="ams-stat"><span class="sun">&#9728;</span></div>
-              </div>
-              <div class="slot-labels">
-                <div class="slot-label">A1&#8595;</div>
-                <div class="slot-label">A2&#8595;</div>
-                <div class="slot-label">A3&#8595;</div>
-                <div class="slot-label">A4&#8595;</div>
-              </div>
-              <div class="ams-slots">
-                <div class="ams-slot active">
-                  <div class="ams-slot-fill" style="background: #FFD700;">
-                    <span class="ams-slot-type dark">PLA</span>
-                    <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #333;">
-                    <span class="ams-slot-type">PETG</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #8B4513;">
-                    <span class="ams-slot-type">PETG</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #666;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="ams-connectors">
-                <div class="connector-group">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-              </div>
-            </div>
-
-            <div class="ams-unit">
-              <div class="ams-header">
-                <div class="ams-stat"><img src="icons/water.svg"> 14%</div>
-                <div class="ams-stat">24°C</div>
-                <div class="ams-stat"><span class="sun">&#9728;</span></div>
-              </div>
-              <div class="slot-labels">
-                <div class="slot-label">B1&#8595;</div>
-                <div class="slot-label">B2&#8595;</div>
-                <div class="slot-label">B3&#8595;</div>
-                <div class="slot-label">B4&#8595;</div>
-              </div>
-              <div class="ams-slots">
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #FF6600;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #00AA00;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #0066FF;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #FF0000;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="ams-connectors">
-                <div class="connector-group">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Row 2: AMS 3 + AMS 4 -->
-          <div class="ams-row">
-            <div class="ams-unit">
-              <div class="ams-header">
-                <div class="ams-stat"><img src="icons/water.svg"> 22%</div>
-                <div class="ams-stat">26°C</div>
-                <div class="ams-stat"><span class="sun">&#9728;</span></div>
-              </div>
-              <div class="slot-labels">
-                <div class="slot-label">C1&#8595;</div>
-                <div class="slot-label">C2&#8595;</div>
-                <div class="slot-label">C3&#8595;</div>
-                <div class="slot-label">C4&#8595;</div>
-              </div>
-              <div class="ams-slots">
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #FFFFFF;">
-                    <span class="ams-slot-type dark">PLA</span>
-                    <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #333;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #666;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #999;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="ams-connectors">
-                <div class="connector-group">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-              </div>
-            </div>
-
-            <div class="ams-unit">
-              <div class="ams-header">
-                <div class="ams-stat"><img src="icons/water.svg"> 19%</div>
-                <div class="ams-stat">25°C</div>
-                <div class="ams-stat"><span class="sun">&#9728;</span></div>
-              </div>
-              <div class="slot-labels">
-                <div class="slot-label">D1&#8595;</div>
-                <div class="slot-label">D2&#8595;</div>
-                <div class="slot-label">D3&#8595;</div>
-                <div class="slot-label">D4&#8595;</div>
-              </div>
-              <div class="ams-slots">
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #CC99FF;">
-                    <span class="ams-slot-type dark">PLA</span>
-                    <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #FFCC00;">
-                    <span class="ams-slot-type dark">PLA</span>
-                    <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #00CCCC;">
-                    <span class="ams-slot-type">PLA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #FF66CC;">
-                    <span class="ams-slot-type dark">PLA</span>
-                    <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="ams-connectors">
-                <div class="connector-group">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Row 3: AMS-HT 1 + AMS-HT 2 -->
-          <div class="ams-row">
-            <div class="ams-unit ams-ht">
-              <div class="ams-header">
-                <div class="ams-stat"><img src="icons/water.svg"> 12%</div>
-                <div class="ams-stat">28°C</div>
-              </div>
-              <div class="slot-labels">
-                <div class="slot-label">HT1&#8595;</div>
-              </div>
-              <div class="ams-slots">
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #8844AA;">
-                    <span class="ams-slot-type">ABS</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="ams-connectors">
-                <div class="connector-group">
-                  <div class="connector-line"><div class="vline"></div></div>
-                </div>
-              </div>
-            </div>
-
-            <div class="ams-unit ams-ht">
-              <div class="ams-header">
-                <div class="ams-stat"><img src="icons/water.svg"> 15%</div>
-                <div class="ams-stat">27°C</div>
-              </div>
-              <div class="slot-labels">
-                <div class="slot-label">HT2&#8595;</div>
-              </div>
-              <div class="ams-slots">
-                <div class="ams-slot">
-                  <div class="ams-slot-fill" style="background: #44AA88;">
-                    <span class="ams-slot-type">ASA</span>
-                    <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="ams-connectors">
-                <div class="connector-group">
-                  <div class="connector-line"><div class="vline"></div></div>
-                </div>
-              </div>
-            </div>
-
-            <div style="flex: 1;"></div>
-          </div>
-        </div>
-
-        <!-- AMS Actions -->
-        <div class="ams-actions">
-          <button class="auto-refill-btn">Auto-refill</button>
-          <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-          <div class="hub-section">
-            <div class="hub-line"></div>
-            <div class="hub-graphic">
-              <div class="hub-port"></div>
-              <div class="hub-port"></div>
-            </div>
-            <div class="hub-line"></div>
-          </div>
-          <div class="ams-spacer"></div>
-          <button class="ams-action-btn">Unload</button>
-          <button class="ams-action-btn">Load</button>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1138
mockup/control-page-v13.html

@@ -1,1138 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v13</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f5f5f5;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 580px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-thumb img {
-      width: 100%;
-      height: 100%;
-      object-fit: cover;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== FILAMENT SELECTOR BOXES ========== */
-    .filament-selector-row {
-      display: flex;
-      gap: 10px;
-      margin-bottom: 8px;
-    }
-
-    .filament-box {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      padding: 10px 12px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-    }
-
-    .filament-box:hover { border-color: #ccc; }
-    .filament-box.active { border-color: var(--accent); }
-
-    .filament-dots { display: flex; gap: 4px; }
-
-    .f-dot {
-      width: 18px;
-      height: 18px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .f-dot.empty {
-      background: #e8e8e8 !important;
-      border: 1px solid #ccc;
-    }
-
-    .f-dot-sep {
-      width: 1px;
-      height: 18px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .filament-count {
-      font-size: 13px;
-      color: var(--text-muted);
-      margin-left: 6px;
-    }
-
-    /* AMS Tab Selector Row */
-    .ams-tab-row {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 12px;
-      flex-wrap: wrap;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 6px 10px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .ams-tab:hover { background: var(--bg-hover); }
-    .ams-tab.active { border-color: var(--accent); background: #f0faf3; }
-
-    .ams-tab .tab-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .tab-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .tab-dot.empty {
-      background: #e0e0e0;
-    }
-
-    .ams-tab-label {
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    /* ========== AMS DISPLAY SECTION ========== */
-    .ams-display {
-      display: flex;
-      gap: 16px;
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Left side: AMS unit */
-    .ams-unit-panel {
-      flex: 1;
-    }
-
-    /* AMS Header - humidity + sun only */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 12px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 16px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 8px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 11px;
-      color: var(--text-muted);
-      padding: 4px 0;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 70px;
-      border-radius: 10px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 8px;
-    }
-
-    .ams-slot-type {
-      font-size: 11px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 4px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 16px; height: 16px; }
-    .ams-slot-eye img { width: 14px; height: 14px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Connector Lines below slots */
-    .ams-connectors {
-      display: flex;
-      justify-content: center;
-      padding: 0 12px;
-      position: relative;
-    }
-
-    .connector-group {
-      flex: 1;
-      display: flex;
-      gap: 6px;
-      position: relative;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 14px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 10%;
-      right: 10%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* ========== EXTERNAL SPOOL SECTION ========== */
-    .ext-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      min-width: 140px;
-    }
-
-    .ext-label {
-      font-size: 14px;
-      font-weight: 500;
-      color: var(--text-secondary);
-      margin-bottom: 12px;
-    }
-
-    .ext-content {
-      display: flex;
-      gap: 12px;
-      align-items: flex-start;
-    }
-
-    .ext-slot-wrapper {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .ext-slot {
-      width: 60px;
-      height: 70px;
-      border-radius: 10px;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      margin-bottom: 10px;
-    }
-
-    .ext-slot:hover { border-color: #bbb; }
-
-    .ext-slot .question {
-      font-size: 24px;
-      color: var(--text-muted);
-      margin-bottom: 4px;
-    }
-
-    .ext-slot .diagonal {
-      font-size: 20px;
-      color: var(--text-muted);
-    }
-
-    .ext-connector {
-      width: 2px;
-      height: 14px;
-      background: #c0c0c0;
-    }
-
-    /* External Spool Holder Illustration */
-    .ext-spool-holder {
-      width: 100px;
-      height: 90px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ext-spool-holder svg {
-      width: 100%;
-      height: 100%;
-    }
-
-    /* ========== HUB CONNECTOR SECTION ========== */
-    .hub-connector-section {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 8px;
-      padding: 12px 0;
-    }
-
-    .hub-line-left {
-      flex: 1;
-      height: 2px;
-      background: #c0c0c0;
-      margin-right: -4px;
-    }
-
-    .hub-graphic {
-      display: flex;
-      gap: 4px;
-      padding: 8px 12px;
-      background: #e8e8e8;
-      border-radius: 4px;
-      border: 1px solid var(--border);
-    }
-
-    .hub-port {
-      width: 14px;
-      height: 20px;
-      background: var(--accent);
-      border-radius: 2px;
-    }
-
-    .hub-line-right {
-      flex: 1;
-      height: 2px;
-      background: #c0c0c0;
-      margin-left: -4px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Filament Selector Boxes (Top Row - Large) -->
-        <div class="filament-selector-row">
-          <div class="filament-box active">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FFD700;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #8B4513;"></div>
-              <div class="f-dot" style="background: #666;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FF6600;"></div>
-              <div class="f-dot" style="background: #00AA00;"></div>
-              <div class="f-dot" style="background: #0066FF;"></div>
-              <div class="f-dot" style="background: #FF0000;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-        </div>
-
-        <!-- AMS Tab Selector Row -->
-        <div class="ams-tab-row">
-          <div class="ams-tab active">
-            <div class="tab-dots">
-              <div class="tab-dot" style="background: #FFD700;"></div>
-              <div class="tab-dot" style="background: #333;"></div>
-              <div class="tab-dot" style="background: #8B4513;"></div>
-              <div class="tab-dot" style="background: #666;"></div>
-            </div>
-            <span class="ams-tab-label">AMS 1</span>
-          </div>
-          <div class="ams-tab">
-            <div class="tab-dots">
-              <div class="tab-dot" style="background: #FF6600;"></div>
-              <div class="tab-dot" style="background: #00AA00;"></div>
-              <div class="tab-dot" style="background: #0066FF;"></div>
-              <div class="tab-dot" style="background: #FF0000;"></div>
-            </div>
-            <span class="ams-tab-label">AMS 2</span>
-          </div>
-          <div class="ams-tab">
-            <div class="tab-dots">
-              <div class="tab-dot empty"></div>
-              <div class="tab-dot empty"></div>
-              <div class="tab-dot empty"></div>
-              <div class="tab-dot empty"></div>
-            </div>
-            <span class="ams-tab-label">AMS 3</span>
-          </div>
-          <div class="ams-tab">
-            <div class="tab-dots">
-              <div class="tab-dot" style="background: #8844AA;"></div>
-              <div class="tab-dot" style="background: #8844AA;"></div>
-            </div>
-            <span class="ams-tab-label">HT 1</span>
-          </div>
-          <div class="ams-tab">
-            <div class="tab-dots">
-              <div class="tab-dot" style="background: #44AA88;"></div>
-              <div class="tab-dot" style="background: #44AA88;"></div>
-            </div>
-            <span class="ams-tab-label">HT 2</span>
-          </div>
-        </div>
-
-        <!-- AMS Display Panel -->
-        <div class="ams-display">
-          <!-- AMS Unit (shows currently selected AMS) -->
-          <div class="ams-unit-panel">
-            <!-- AMS Header: Humidity + Sun (NO temperature) -->
-            <div class="ams-header">
-              <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-              <div class="ams-stat"><span class="sun">&#9788;</span></div>
-            </div>
-
-            <!-- Slot Labels -->
-            <div class="slot-labels">
-              <div class="slot-label">A1↓</div>
-              <div class="slot-label">A2↓</div>
-              <div class="slot-label">A3↓</div>
-              <div class="slot-label">A4↓</div>
-            </div>
-
-            <!-- AMS Slots -->
-            <div class="ams-slots">
-              <div class="ams-slot active">
-                <div class="ams-slot-fill" style="background: #FFD700;">
-                  <span class="ams-slot-type dark">PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #333;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #8B4513;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- Connector Lines -->
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- External Spool Section -->
-          <div class="ext-section">
-            <div class="ext-label">Ext</div>
-            <div class="ext-content">
-              <div class="ext-slot-wrapper">
-                <div class="ext-slot">
-                  <span class="question">?</span>
-                  <span class="diagonal">⁄</span>
-                </div>
-                <div class="ext-connector"></div>
-              </div>
-              <!-- External Spool Holder Illustration -->
-              <div class="ext-spool-holder">
-                <svg viewBox="0 0 100 90" fill="none" xmlns="http://www.w3.org/2000/svg">
-                  <!-- Spool holder box -->
-                  <path d="M10 30 L60 15 L90 30 L90 75 L60 90 L10 75 Z" fill="#f0f0f0" stroke="#ccc" stroke-width="1.5"/>
-                  <path d="M10 30 L60 45 L90 30" fill="none" stroke="#ccc" stroke-width="1.5"/>
-                  <path d="M60 45 L60 90" fill="none" stroke="#ccc" stroke-width="1.5"/>
-                  <!-- Vent lines -->
-                  <line x1="70" y1="50" x2="85" y2="45" stroke="#ddd" stroke-width="1"/>
-                  <line x1="70" y1="58" x2="85" y2="53" stroke="#ddd" stroke-width="1"/>
-                  <line x1="70" y1="66" x2="85" y2="61" stroke="#ddd" stroke-width="1"/>
-                  <!-- Spool -->
-                  <ellipse cx="35" cy="35" rx="20" ry="8" fill="#00ae42" stroke="#008833" stroke-width="1.5"/>
-                  <rect x="15" y="35" width="40" height="12" fill="#00ae42"/>
-                  <ellipse cx="35" cy="47" rx="20" ry="8" fill="#009938" stroke="#008833" stroke-width="1.5"/>
-                  <!-- Spool center hole -->
-                  <ellipse cx="35" cy="35" rx="8" ry="3" fill="#e0e0e0"/>
-                  <ellipse cx="35" cy="47" rx="8" ry="3" fill="#ccc"/>
-                  <!-- PTFE tube -->
-                  <path d="M35 55 Q35 65 50 70 L55 68" fill="none" stroke="#00ae42" stroke-width="3"/>
-                </svg>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Hub Connector -->
-        <div class="hub-connector-section">
-          <div class="hub-line-left"></div>
-          <div class="hub-graphic">
-            <div class="hub-port"></div>
-            <div class="hub-port"></div>
-          </div>
-          <div class="hub-line-right"></div>
-        </div>
-
-        <!-- AMS Actions -->
-        <div class="ams-actions">
-          <button class="auto-refill-btn">Auto-refill</button>
-          <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-          <div class="ams-spacer"></div>
-          <button class="ams-action-btn">Unload</button>
-          <button class="ams-action-btn">Load</button>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1055
mockup/control-page-v14.html

@@ -1,1055 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v14</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 600px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION - LEFT/RIGHT SPLIT ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    .ams-split-layout {
-      display: flex;
-      gap: 0;
-    }
-
-    /* Left and Right AMS Panels */
-    .ams-side {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-side-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Device Selectors (small icons) */
-    .ams-device-selectors {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 12px;
-      justify-content: center;
-      flex-wrap: wrap;
-    }
-
-    .ams-device-icon {
-      position: relative;
-      width: 50px;
-      height: 40px;
-      cursor: pointer;
-      border: 2px solid transparent;
-      border-radius: 6px;
-      padding: 2px;
-      transition: border-color 0.2s;
-    }
-
-    .ams-device-icon:hover {
-      border-color: #ccc;
-    }
-
-    .ams-device-icon.active {
-      border-color: var(--accent);
-      background: #f0faf3;
-    }
-
-    .ams-device-icon img {
-      width: 100%;
-      height: 100%;
-      object-fit: contain;
-    }
-
-    /* Colored spool overlays on AMS icons */
-    .ams-device-icon .spool-colors {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-device-icon .spool-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      border: 1px solid rgba(0,0,0,0.2);
-    }
-
-    .ams-device-icon.ams-ht-icon .spool-colors {
-      gap: 0;
-    }
-
-    .ams-device-icon.ams-ht-icon .spool-dot {
-      width: 10px;
-      height: 10px;
-    }
-
-    /* Center Hub/Extruder Divider */
-    .ams-center-divider {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      padding: 0 16px;
-      min-width: 80px;
-    }
-
-    .hub-connector {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .hub-ports {
-      display: flex;
-      gap: 4px;
-      padding: 6px 10px;
-      background: #e8e8e8;
-      border-radius: 4px;
-      border: 1px solid var(--border);
-    }
-
-    .hub-port {
-      width: 12px;
-      height: 18px;
-      background: var(--accent);
-      border-radius: 2px;
-    }
-
-    .extruder-divider-img {
-      width: 50px;
-      margin-top: 8px;
-    }
-
-    .extruder-divider-img img {
-      width: 100%;
-    }
-
-    /* AMS Detail Display */
-    .ams-detail {
-      background: var(--bg-white);
-      border-radius: 8px;
-      padding: 12px;
-      min-height: 140px;
-    }
-
-    .ams-detail-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 4px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 55px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 2px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Empty slot */
-    .ams-slot.empty {
-      background: var(--bg-panel);
-    }
-
-    .ams-slot.empty .ams-slot-fill {
-      justify-content: center;
-      color: var(--text-muted);
-      font-size: 18px;
-    }
-
-    /* External Spool */
-    .ext-slot-container {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      margin-top: 8px;
-    }
-
-    .ext-label {
-      font-size: 10px;
-      color: var(--text-muted);
-      margin-bottom: 4px;
-    }
-
-    .ext-slot {
-      width: 45px;
-      height: 55px;
-      border-radius: 8px;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      font-size: 16px;
-      color: var(--text-muted);
-    }
-
-    .ext-slot:hover { border-color: #bbb; }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 12px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section - Left/Right Split -->
-        <div class="ams-section">
-          <div class="ams-split-layout">
-            <!-- LEFT SIDE -->
-            <div class="ams-side">
-              <div class="ams-side-label">Left Nozzle</div>
-
-              <!-- AMS Device Selectors -->
-              <div class="ams-device-selectors">
-                <div class="ams-device-icon active" title="AMS 1">
-                  <img src="icons/ams.png">
-                  <div class="spool-colors">
-                    <div class="spool-dot" style="background: #FFD700;"></div>
-                    <div class="spool-dot" style="background: #333;"></div>
-                    <div class="spool-dot" style="background: #8B4513;"></div>
-                    <div class="spool-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-device-icon" title="AMS 2">
-                  <img src="icons/ams.png">
-                  <div class="spool-colors">
-                    <div class="spool-dot" style="background: #FF6600;"></div>
-                    <div class="spool-dot" style="background: #00AA00;"></div>
-                    <div class="spool-dot" style="background: #0066FF;"></div>
-                    <div class="spool-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-device-icon ams-ht-icon" title="AMS-HT 1">
-                  <img src="icons/ams-ht.png">
-                  <div class="spool-colors">
-                    <div class="spool-dot" style="background: #8844AA;"></div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- AMS Detail Display -->
-              <div class="ams-detail">
-                <div class="ams-detail-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18%</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- External Spool -->
-              <div class="ext-slot-container">
-                <div class="ext-label">Ext</div>
-                <div class="ext-slot">?<br>/</div>
-              </div>
-            </div>
-
-            <!-- CENTER DIVIDER - Hub & Extruder -->
-            <div class="ams-center-divider">
-              <div class="hub-connector">
-                <div class="hub-ports">
-                  <div class="hub-port"></div>
-                  <div class="hub-port"></div>
-                </div>
-                <div class="extruder-divider-img">
-                  <img src="icons/dual-extruder.png">
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT SIDE -->
-            <div class="ams-side">
-              <div class="ams-side-label">Right Nozzle</div>
-
-              <!-- AMS Device Selectors -->
-              <div class="ams-device-selectors">
-                <div class="ams-device-icon active" title="AMS 3">
-                  <img src="icons/ams.png">
-                  <div class="spool-colors">
-                    <div class="spool-dot" style="background: #FFFFFF; border-color: #ccc;"></div>
-                    <div class="spool-dot" style="background: #222;"></div>
-                    <div class="spool-dot" style="background: #666;"></div>
-                    <div class="spool-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-device-icon" title="AMS 4">
-                  <img src="icons/ams.png">
-                  <div class="spool-colors">
-                    <div class="spool-dot" style="background: #CC99FF;"></div>
-                    <div class="spool-dot" style="background: #FFCC00;"></div>
-                    <div class="spool-dot" style="background: #00CCCC;"></div>
-                    <div class="spool-dot" style="background: #FF66CC;"></div>
-                  </div>
-                </div>
-                <div class="ams-device-icon ams-ht-icon" title="AMS-HT 2">
-                  <img src="icons/ams-ht.png">
-                  <div class="spool-colors">
-                    <div class="spool-dot" style="background: #44AA88;"></div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- AMS Detail Display -->
-              <div class="ams-detail">
-                <div class="ams-detail-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14%</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-                <div class="slot-labels">
-                  <div class="slot-label">C1↓</div>
-                  <div class="slot-label">C2↓</div>
-                  <div class="slot-label">C3↓</div>
-                  <div class="slot-label">C4↓</div>
-                </div>
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- External Spool -->
-              <div class="ext-slot-container">
-                <div class="ext-label">Ext</div>
-                <div class="ext-slot">?<br>/</div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1091
mockup/control-page-v15.html

@@ -1,1091 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v15</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 620px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION - LEFT/RIGHT SPLIT ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    .ams-split-layout {
-      display: flex;
-      gap: 0;
-    }
-
-    /* Left and Right AMS Panels */
-    .ams-side {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-side-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Device Selectors (small icons) - 2 rows */
-    .ams-device-selectors {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-      margin-bottom: 12px;
-      align-items: center;
-    }
-
-    .ams-device-row {
-      display: flex;
-      gap: 4px;
-      justify-content: center;
-    }
-
-    .ams-device-icon {
-      position: relative;
-      width: 44px;
-      height: 32px;
-      cursor: pointer;
-      border: 2px solid transparent;
-      border-radius: 4px;
-      padding: 2px;
-      transition: border-color 0.2s;
-      background: var(--bg-white);
-    }
-
-    .ams-device-icon:hover {
-      border-color: #ccc;
-    }
-
-    .ams-device-icon.active {
-      border-color: var(--accent);
-      background: #f0faf3;
-    }
-
-    .ams-device-icon.empty {
-      opacity: 0.4;
-    }
-
-    .ams-device-icon img {
-      width: 100%;
-      height: 100%;
-      object-fit: contain;
-    }
-
-    /* Colored spool overlays on AMS icons */
-    .ams-device-icon .spool-colors {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      display: flex;
-      gap: 1px;
-    }
-
-    .ams-device-icon .spool-dot {
-      width: 6px;
-      height: 6px;
-      border-radius: 50%;
-      border: 1px solid rgba(0,0,0,0.2);
-    }
-
-    .ams-device-icon.ams-ht-icon .spool-colors {
-      gap: 0;
-    }
-
-    .ams-device-icon.ams-ht-icon .spool-dot {
-      width: 8px;
-      height: 8px;
-    }
-
-    /* Center Hub/Extruder Divider */
-    .ams-center-divider {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      padding: 0 12px;
-      min-width: 70px;
-    }
-
-    .hub-connector {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .hub-ports {
-      display: flex;
-      gap: 3px;
-      padding: 5px 8px;
-      background: #e8e8e8;
-      border-radius: 4px;
-      border: 1px solid var(--border);
-    }
-
-    .hub-port {
-      width: 10px;
-      height: 16px;
-      background: var(--accent);
-      border-radius: 2px;
-    }
-
-    .extruder-divider-img {
-      width: 45px;
-      margin-top: 6px;
-    }
-
-    .extruder-divider-img img {
-      width: 100%;
-    }
-
-    /* AMS Detail Display */
-    .ams-detail {
-      background: var(--bg-white);
-      border-radius: 8px;
-      padding: 10px;
-      min-height: 120px;
-    }
-
-    .ams-detail-header {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 3px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 3px;
-      margin-bottom: 4px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 9px;
-      color: var(--text-muted);
-      padding: 2px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 3px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 50px;
-      border-radius: 6px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 4px;
-    }
-
-    .ams-slot-type {
-      font-size: 8px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 2px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 10px; height: 10px; }
-    .ams-slot-eye img { width: 9px; height: 9px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Empty slot */
-    .ams-slot.empty {
-      background: var(--bg-panel);
-    }
-
-    .ams-slot.empty .ams-slot-fill {
-      justify-content: center;
-      color: var(--text-muted);
-      font-size: 16px;
-    }
-
-    /* External Spool */
-    .ext-slot-container {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      margin-top: 8px;
-    }
-
-    .ext-label {
-      font-size: 9px;
-      color: var(--text-muted);
-      margin-bottom: 3px;
-    }
-
-    .ext-slot {
-      width: 40px;
-      height: 50px;
-      border-radius: 6px;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      line-height: 1;
-    }
-
-    .ext-slot:hover { border-color: #bbb; }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 12px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section - Left/Right Split -->
-        <div class="ams-section">
-          <div class="ams-split-layout">
-            <!-- LEFT SIDE -->
-            <div class="ams-side">
-              <div class="ams-side-label">Left Nozzle</div>
-
-              <!-- AMS Device Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-device-selectors">
-                <!-- Row 1: AMS 1, AMS 2 -->
-                <div class="ams-device-row">
-                  <div class="ams-device-icon active" title="AMS 1">
-                    <img src="icons/ams.png">
-                    <div class="spool-colors">
-                      <div class="spool-dot" style="background: #FFD700;"></div>
-                      <div class="spool-dot" style="background: #333;"></div>
-                      <div class="spool-dot" style="background: #8B4513;"></div>
-                      <div class="spool-dot" style="background: #666;"></div>
-                    </div>
-                  </div>
-                  <div class="ams-device-icon" title="AMS 2">
-                    <img src="icons/ams.png">
-                    <div class="spool-colors">
-                      <div class="spool-dot" style="background: #FF6600;"></div>
-                      <div class="spool-dot" style="background: #00AA00;"></div>
-                      <div class="spool-dot" style="background: #0066FF;"></div>
-                      <div class="spool-dot" style="background: #FF0000;"></div>
-                    </div>
-                  </div>
-                </div>
-                <!-- Row 2: AMS 3, AMS 4, AMS-HT -->
-                <div class="ams-device-row">
-                  <div class="ams-device-icon empty" title="AMS 3 (empty)">
-                    <img src="icons/ams.png">
-                  </div>
-                  <div class="ams-device-icon empty" title="AMS 4 (empty)">
-                    <img src="icons/ams.png">
-                  </div>
-                  <div class="ams-device-icon ams-ht-icon" title="AMS-HT">
-                    <img src="icons/ams-ht.png">
-                    <div class="spool-colors">
-                      <div class="spool-dot" style="background: #8844AA;"></div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- AMS Detail Display -->
-              <div class="ams-detail">
-                <div class="ams-detail-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18%</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- External Spool -->
-              <div class="ext-slot-container">
-                <div class="ext-label">Ext</div>
-                <div class="ext-slot">?<br>/</div>
-              </div>
-            </div>
-
-            <!-- CENTER DIVIDER - Hub & Extruder -->
-            <div class="ams-center-divider">
-              <div class="hub-connector">
-                <div class="hub-ports">
-                  <div class="hub-port"></div>
-                  <div class="hub-port"></div>
-                </div>
-                <div class="extruder-divider-img">
-                  <img src="icons/dual-extruder.png">
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT SIDE -->
-            <div class="ams-side">
-              <div class="ams-side-label">Right Nozzle</div>
-
-              <!-- AMS Device Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-device-selectors">
-                <!-- Row 1: AMS 5, AMS 6 -->
-                <div class="ams-device-row">
-                  <div class="ams-device-icon active" title="AMS 5">
-                    <img src="icons/ams.png">
-                    <div class="spool-colors">
-                      <div class="spool-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                      <div class="spool-dot" style="background: #222;"></div>
-                      <div class="spool-dot" style="background: #666;"></div>
-                      <div class="spool-dot" style="background: #999;"></div>
-                    </div>
-                  </div>
-                  <div class="ams-device-icon" title="AMS 6">
-                    <img src="icons/ams.png">
-                    <div class="spool-colors">
-                      <div class="spool-dot" style="background: #CC99FF;"></div>
-                      <div class="spool-dot" style="background: #FFCC00;"></div>
-                      <div class="spool-dot" style="background: #00CCCC;"></div>
-                      <div class="spool-dot" style="background: #FF66CC;"></div>
-                    </div>
-                  </div>
-                </div>
-                <!-- Row 2: AMS 7, AMS 8, AMS-HT -->
-                <div class="ams-device-row">
-                  <div class="ams-device-icon empty" title="AMS 7 (empty)">
-                    <img src="icons/ams.png">
-                  </div>
-                  <div class="ams-device-icon empty" title="AMS 8 (empty)">
-                    <img src="icons/ams.png">
-                  </div>
-                  <div class="ams-device-icon ams-ht-icon" title="AMS-HT">
-                    <img src="icons/ams-ht.png">
-                    <div class="spool-colors">
-                      <div class="spool-dot" style="background: #44AA88;"></div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- AMS Detail Display -->
-              <div class="ams-detail">
-                <div class="ams-detail-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14%</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-                <div class="slot-labels">
-                  <div class="slot-label">E1↓</div>
-                  <div class="slot-label">E2↓</div>
-                  <div class="slot-label">E3↓</div>
-                  <div class="slot-label">E4↓</div>
-                </div>
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- External Spool -->
-              <div class="ext-slot-container">
-                <div class="ext-label">Ext</div>
-                <div class="ext-slot">?<br>/</div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1206
mockup/control-page-v16.html

@@ -1,1206 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v16</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 620px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle: two panels side by side */
-    .ams-dual-layout {
-      display: flex;
-      gap: 12px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Selector Row - colored boxes */
-    .ams-selector-row {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 12px;
-      flex-wrap: wrap;
-    }
-
-    .ams-selector-box {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      padding: 8px 10px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .ams-selector-box:hover { border-color: #ccc; }
-    .ams-selector-box.active { border-color: var(--accent); }
-
-    .ams-selector-box .color-dots {
-      display: flex;
-      gap: 3px;
-    }
-
-    .ams-selector-box .color-dot {
-      width: 14px;
-      height: 14px;
-      border-radius: 3px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-selector-box .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    .ams-selector-box .separator {
-      width: 1px;
-      height: 14px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .ams-selector-box .count {
-      font-size: 12px;
-      color: var(--text-muted);
-      margin-left: 4px;
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 14px;
-      display: flex;
-      gap: 12px;
-    }
-
-    /* AMS Slots Area */
-    .ams-slots-area {
-      flex: 1;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 16px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 8px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 11px;
-      color: var(--text-muted);
-      padding: 4px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 2px;
-    }
-
-    .slot-label .arrow {
-      font-size: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 70px;
-      border-radius: 10px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 6px;
-    }
-
-    .ams-slot-type {
-      font-size: 10px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 4px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 14px; height: 14px; }
-    .ams-slot-eye img { width: 12px; height: 12px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Slot Connectors */
-    .slot-connectors {
-      display: flex;
-      padding: 0 10px;
-      position: relative;
-      height: 20px;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 12px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 8px;
-      left: 15%;
-      right: 15%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    .connector-down {
-      position: absolute;
-      bottom: 0;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 10px;
-      background: #c0c0c0;
-    }
-
-    /* External Spool Section */
-    .ext-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      min-width: 100px;
-    }
-
-    .ext-label {
-      font-size: 14px;
-      font-weight: 500;
-      color: var(--text-secondary);
-      margin-bottom: 10px;
-    }
-
-    .ext-content {
-      display: flex;
-      gap: 8px;
-      align-items: flex-start;
-    }
-
-    .ext-slot-wrapper {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .ext-slot {
-      width: 55px;
-      height: 70px;
-      border-radius: 10px;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-    }
-
-    .ext-slot:hover { border-color: #bbb; }
-
-    .ext-slot .question {
-      font-size: 22px;
-      color: var(--text-muted);
-    }
-
-    .ext-slot .slash {
-      font-size: 18px;
-      color: var(--text-muted);
-      margin-top: -4px;
-    }
-
-    .ext-connector {
-      width: 2px;
-      height: 20px;
-      background: #c0c0c0;
-      margin-top: 0;
-    }
-
-    /* Spool Holder Illustration */
-    .spool-holder {
-      width: 70px;
-      height: 80px;
-    }
-
-    .spool-holder svg {
-      width: 100%;
-      height: 100%;
-    }
-
-    /* Hub Connector */
-    .hub-row {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      padding: 8px 0;
-      margin-top: 4px;
-    }
-
-    .hub-line {
-      flex: 1;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    .hub-graphic {
-      display: flex;
-      gap: 4px;
-      padding: 6px 12px;
-      background: #e8e8e8;
-      border-radius: 4px;
-      border: 1px solid var(--border);
-    }
-
-    .hub-port {
-      width: 12px;
-      height: 18px;
-      background: var(--accent);
-      border-radius: 2px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 14px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section - Dual Nozzle Layout -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Selector Boxes -->
-              <div class="ams-selector-row">
-                <div class="ams-selector-box active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-selector-box">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                  </div>
-                  <div class="separator"></div>
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                  <span class="count">0</span>
-                </div>
-                <div class="ams-selector-box">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                  <span class="count">0</span>
-                </div>
-              </div>
-
-              <!-- AMS Content: Slots + Ext -->
-              <div class="ams-content">
-                <!-- AMS Slots Area -->
-                <div class="ams-slots-area">
-                  <div class="ams-header">
-                    <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                    <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                  </div>
-
-                  <div class="slot-labels">
-                    <div class="slot-label">A1<span class="arrow">↓</span></div>
-                    <div class="slot-label">A2<span class="arrow">↓</span></div>
-                    <div class="slot-label">A3<span class="arrow">↓</span></div>
-                    <div class="slot-label">A4<span class="arrow">↓</span></div>
-                  </div>
-
-                  <div class="ams-slots">
-                    <div class="ams-slot active">
-                      <div class="ams-slot-fill" style="background: #FFD700;">
-                        <span class="ams-slot-type dark">PLA</span>
-                        <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #333;">
-                        <span class="ams-slot-type">PETG</span>
-                        <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #8B4513;">
-                        <span class="ams-slot-type">PETG</span>
-                        <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #666;">
-                        <span class="ams-slot-type">PLA</span>
-                        <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                  </div>
-
-                  <!-- Slot Connectors -->
-                  <div class="slot-connectors">
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-hbar"></div>
-                    <div class="connector-down"></div>
-                  </div>
-                </div>
-
-                <!-- External Spool Section -->
-                <div class="ext-section">
-                  <div class="ext-label">Ext</div>
-                  <div class="ext-content">
-                    <div class="ext-slot-wrapper">
-                      <div class="ext-slot">
-                        <span class="question">?</span>
-                        <span class="slash">⁄</span>
-                      </div>
-                      <div class="ext-connector"></div>
-                    </div>
-                    <div class="spool-holder">
-                      <svg viewBox="0 0 70 80" fill="none" xmlns="http://www.w3.org/2000/svg">
-                        <!-- Spool holder box -->
-                        <path d="M5 25 L40 12 L65 25 L65 65 L40 78 L5 65 Z" fill="#f0f0f0" stroke="#ccc" stroke-width="1.5"/>
-                        <path d="M5 25 L40 38 L65 25" fill="none" stroke="#ccc" stroke-width="1.5"/>
-                        <path d="M40 38 L40 78" fill="none" stroke="#ccc" stroke-width="1.5"/>
-                        <!-- Vent lines -->
-                        <line x1="48" y1="45" x2="60" y2="40" stroke="#ddd" stroke-width="1"/>
-                        <line x1="48" y1="52" x2="60" y2="47" stroke="#ddd" stroke-width="1"/>
-                        <line x1="48" y1="59" x2="60" y2="54" stroke="#ddd" stroke-width="1"/>
-                        <!-- Spool -->
-                        <ellipse cx="25" cy="28" rx="15" ry="6" fill="#00ae42" stroke="#008833" stroke-width="1"/>
-                        <rect x="10" y="28" width="30" height="10" fill="#00ae42"/>
-                        <ellipse cx="25" cy="38" rx="15" ry="6" fill="#009938" stroke="#008833" stroke-width="1"/>
-                        <!-- Spool center -->
-                        <ellipse cx="25" cy="28" rx="6" ry="2.5" fill="#e0e0e0"/>
-                        <ellipse cx="25" cy="38" rx="6" ry="2.5" fill="#ccc"/>
-                        <!-- PTFE tube -->
-                        <path d="M25 44 Q25 52 35 56 L40 54" fill="none" stroke="#00ae42" stroke-width="2.5"/>
-                      </svg>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- Hub Row -->
-              <div class="hub-row">
-                <div class="hub-line"></div>
-                <div class="hub-graphic">
-                  <div class="hub-port"></div>
-                  <div class="hub-port"></div>
-                </div>
-                <div class="hub-line"></div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Selector Boxes -->
-              <div class="ams-selector-row">
-                <div class="ams-selector-box active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-selector-box">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                  <span class="count">0</span>
-                </div>
-              </div>
-
-              <!-- AMS Content: Slots + Ext -->
-              <div class="ams-content">
-                <!-- AMS Slots Area -->
-                <div class="ams-slots-area">
-                  <div class="ams-header">
-                    <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                    <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                  </div>
-
-                  <div class="slot-labels">
-                    <div class="slot-label">B1<span class="arrow">↓</span></div>
-                    <div class="slot-label">B2<span class="arrow">↓</span></div>
-                    <div class="slot-label">B3<span class="arrow">↓</span></div>
-                    <div class="slot-label">B4<span class="arrow">↓</span></div>
-                  </div>
-
-                  <div class="ams-slots">
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #FFFFFF;">
-                        <span class="ams-slot-type dark">PLA</span>
-                        <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #222;">
-                        <span class="ams-slot-type">PLA</span>
-                        <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #666;">
-                        <span class="ams-slot-type">PLA</span>
-                        <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                    <div class="ams-slot">
-                      <div class="ams-slot-fill" style="background: #999;">
-                        <span class="ams-slot-type">PLA</span>
-                        <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                      </div>
-                    </div>
-                  </div>
-
-                  <!-- Slot Connectors -->
-                  <div class="slot-connectors">
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-hbar"></div>
-                    <div class="connector-down"></div>
-                  </div>
-                </div>
-
-                <!-- External Spool Section -->
-                <div class="ext-section">
-                  <div class="ext-label">Ext</div>
-                  <div class="ext-content">
-                    <div class="ext-slot-wrapper">
-                      <div class="ext-slot">
-                        <span class="question">?</span>
-                        <span class="slash">⁄</span>
-                      </div>
-                      <div class="ext-connector"></div>
-                    </div>
-                    <div class="spool-holder">
-                      <svg viewBox="0 0 70 80" fill="none" xmlns="http://www.w3.org/2000/svg">
-                        <path d="M5 25 L40 12 L65 25 L65 65 L40 78 L5 65 Z" fill="#f0f0f0" stroke="#ccc" stroke-width="1.5"/>
-                        <path d="M5 25 L40 38 L65 25" fill="none" stroke="#ccc" stroke-width="1.5"/>
-                        <path d="M40 38 L40 78" fill="none" stroke="#ccc" stroke-width="1.5"/>
-                        <line x1="48" y1="45" x2="60" y2="40" stroke="#ddd" stroke-width="1"/>
-                        <line x1="48" y1="52" x2="60" y2="47" stroke="#ddd" stroke-width="1"/>
-                        <line x1="48" y1="59" x2="60" y2="54" stroke="#ddd" stroke-width="1"/>
-                        <ellipse cx="25" cy="28" rx="15" ry="6" fill="#00ae42" stroke="#008833" stroke-width="1"/>
-                        <rect x="10" y="28" width="30" height="10" fill="#00ae42"/>
-                        <ellipse cx="25" cy="38" rx="15" ry="6" fill="#009938" stroke="#008833" stroke-width="1"/>
-                        <ellipse cx="25" cy="28" rx="6" ry="2.5" fill="#e0e0e0"/>
-                        <ellipse cx="25" cy="38" rx="6" ry="2.5" fill="#ccc"/>
-                        <path d="M25 44 Q25 52 35 56 L40 54" fill="none" stroke="#00ae42" stroke-width="2.5"/>
-                      </svg>
-                    </div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- Hub Row -->
-              <div class="hub-row">
-                <div class="hub-line"></div>
-                <div class="hub-graphic">
-                  <div class="hub-port"></div>
-                  <div class="hub-port"></div>
-                </div>
-                <div class="hub-line"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1032
mockup/control-page-v17.html

@@ -1,1032 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v18</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 680px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 16px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row - compact */
-    .ams-tab-row {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 5px 6px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 5px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 3px 5px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 28px;
-      height: 22px;
-      object-fit: contain;
-    }
-
-    .ams-tab.ams-ht-tab .ht-dot {
-      position: absolute;
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      bottom: 4px;
-      left: 50%;
-      transform: translateX(-50%);
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 12px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 16px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 75px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Slot Connectors */
-    .slot-connectors {
-      display: flex;
-      padding: 0 8px;
-      position: relative;
-      height: 16px;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 10px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 6px;
-      left: 12%;
-      right: 12%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Extruder Divider - centered at bottom between panels */
-    .extruder-divider {
-      display: flex;
-      justify-content: center;
-      margin-top: 12px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .extruder-divider img {
-      height: 60px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      margin-top: 12px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1↓</div>
-                  <div class="slot-label">B2↓</div>
-                  <div class="slot-label">B3↓</div>
-                  <div class="slot-label">B4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-
-          <!-- Centered Extruder Divider at bottom -->
-          <div class="extruder-divider">
-            <img src="icons/extruder-left-right.png">
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1061
mockup/control-page-v18.html

@@ -1,1061 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v19</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 680px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 16px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row - compact */
-    .ams-tab-row {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 5px 6px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 5px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 3px 5px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 28px;
-      height: 22px;
-      object-fit: contain;
-    }
-
-    .ams-tab.ams-ht-tab .ht-dot {
-      position: absolute;
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      bottom: 4px;
-      left: 50%;
-      transform: translateX(-50%);
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 12px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 16px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 75px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Slot Connectors */
-    .slot-connectors {
-      display: flex;
-      padding: 0 8px;
-      position: relative;
-      height: 20px;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 14px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 6px;
-      left: 10%;
-      right: 10%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Hub rectangle under each panel */
-    .hub-container {
-      display: flex;
-      justify-content: center;
-      margin-top: -2px;
-      margin-bottom: 8px;
-    }
-
-    .hub-rect {
-      width: 30px;
-      height: 12px;
-      background: #d0d0d0;
-      border-radius: 2px;
-      position: relative;
-    }
-
-    .hub-rect::after {
-      content: '';
-      position: absolute;
-      bottom: -20px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 20px;
-      background: #c0c0c0;
-    }
-
-    /* Extruder in actions row */
-    .extruder-center {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-    }
-
-    .extruder-center img {
-      height: 45px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      gap: 10px;
-      margin-top: 20px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-
-                <div class="hub-container">
-                  <div class="hub-rect"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1↓</div>
-                  <div class="slot-label">B2↓</div>
-                  <div class="slot-label">B3↓</div>
-                  <div class="slot-label">B4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-
-                <div class="hub-container">
-                  <div class="hub-rect"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions with Extruder in center -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="extruder-center">
-              <img src="icons/extruder-left-right.png">
-            </div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1061
mockup/control-page-v19.html

@@ -1,1061 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v20</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 680px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 16px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row - compact */
-    .ams-tab-row {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 5px 6px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 5px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 3px 5px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 28px;
-      height: 22px;
-      object-fit: contain;
-    }
-
-    .ams-tab.ams-ht-tab .ht-dot {
-      position: absolute;
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      bottom: 4px;
-      left: 50%;
-      transform: translateX(-50%);
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 12px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 16px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 75px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Slot Connectors */
-    .slot-connectors {
-      display: flex;
-      padding: 0 8px;
-      position: relative;
-      height: 20px;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 14px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 6px;
-      left: 10%;
-      right: 10%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Hub rectangle under each panel */
-    .hub-container {
-      display: flex;
-      justify-content: center;
-      margin-top: -2px;
-      margin-bottom: 8px;
-    }
-
-    .hub-rect {
-      width: 30px;
-      height: 12px;
-      background: #d0d0d0;
-      border-radius: 2px;
-      position: relative;
-    }
-
-    .hub-rect::after {
-      content: '';
-      position: absolute;
-      bottom: -20px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 20px;
-      background: #c0c0c0;
-    }
-
-    /* Extruder in actions row */
-    .extruder-center {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-    }
-
-    .extruder-center img {
-      height: 45px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      gap: 10px;
-      margin-top: 20px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-
-                <div class="hub-container">
-                  <div class="hub-rect"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1↓</div>
-                  <div class="slot-label">B2↓</div>
-                  <div class="slot-label">B3↓</div>
-                  <div class="slot-label">B4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-
-                <div class="hub-container">
-                  <div class="hub-rect"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions with Extruder in center -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="extruder-center">
-              <img src="icons/extruder-left-right.png">
-            </div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1123
mockup/control-page-v2.html

@@ -1,1123 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - Bambu Studio Style</title>
-  <style>
-    * {
-      margin: 0;
-      padding: 0;
-      box-sizing: border-box;
-    }
-
-    :root {
-      --bg-primary: #1a1a1a;
-      --bg-secondary: #2d2d2d;
-      --bg-tertiary: #3d3d3d;
-      --bg-card: #252525;
-      --text-primary: #ffffff;
-      --text-secondary: #888888;
-      --text-muted: #666666;
-      --accent: #00ae42;
-      --accent-hover: #00c94b;
-      --orange: #f5a623;
-      --red: #e74c3c;
-      --border: #404040;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-primary);
-      color: var(--text-primary);
-      min-height: 100vh;
-      font-size: 13px;
-    }
-
-    /* Printer Tabs */
-    .printer-tabs {
-      display: flex;
-      background: var(--bg-secondary);
-      border-bottom: 1px solid var(--border);
-      padding: 0 12px;
-    }
-
-    .printer-tab {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 10px 16px;
-      border: none;
-      background: none;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 13px;
-      border-bottom: 2px solid transparent;
-      margin-bottom: -1px;
-    }
-
-    .printer-tab:hover {
-      color: var(--text-primary);
-    }
-
-    .printer-tab.active {
-      color: var(--text-primary);
-      border-bottom-color: var(--accent);
-    }
-
-    .status-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      background: var(--accent);
-    }
-
-    .status-dot.offline { background: var(--red); }
-    .status-dot.printing { background: var(--orange); }
-
-    .status-badge {
-      font-size: 10px;
-      padding: 2px 6px;
-      background: var(--bg-tertiary);
-      border-radius: 3px;
-      color: var(--text-secondary);
-    }
-
-    /* Main Layout */
-    .main-content {
-      display: grid;
-      grid-template-columns: 1fr 340px;
-      height: calc(100vh - 41px);
-    }
-
-    /* Left Column */
-    .left-column {
-      display: flex;
-      flex-direction: column;
-      background: #000;
-    }
-
-    /* Camera */
-    .camera-section {
-      flex: 1;
-      position: relative;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 100%);
-    }
-
-    .camera-placeholder {
-      color: var(--text-muted);
-      font-size: 14px;
-    }
-
-    .camera-controls {
-      position: absolute;
-      top: 8px;
-      left: 8px;
-      display: flex;
-      gap: 4px;
-    }
-
-    .cam-btn {
-      padding: 4px 10px;
-      background: rgba(0,0,0,0.7);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      color: var(--text-primary);
-      font-size: 11px;
-      cursor: pointer;
-    }
-
-    .cam-btn:hover {
-      background: rgba(0,0,0,0.9);
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-secondary);
-      padding: 12px 16px;
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      border-top: 1px solid var(--border);
-    }
-
-    .print-thumb {
-      width: 56px;
-      height: 56px;
-      background: var(--bg-tertiary);
-      border-radius: 6px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .print-info {
-      flex: 1;
-    }
-
-    .print-name {
-      font-weight: 500;
-      margin-bottom: 6px;
-    }
-
-    .progress-bar {
-      height: 4px;
-      background: var(--bg-tertiary);
-      border-radius: 2px;
-      margin-bottom: 6px;
-    }
-
-    .progress-fill {
-      height: 100%;
-      background: var(--accent);
-      border-radius: 2px;
-      width: 0%;
-    }
-
-    .print-stats {
-      display: flex;
-      gap: 20px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .print-stats span { color: var(--text-primary); }
-
-    .print-btns {
-      display: flex;
-      gap: 6px;
-    }
-
-    .print-btn {
-      width: 36px;
-      height: 36px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .print-btn.pause { background: var(--orange); color: white; }
-    .print-btn.stop { background: var(--red); color: white; }
-    .print-btn:disabled { opacity: 0.4; cursor: not-allowed; }
-
-    /* Right Panel */
-    .control-panel {
-      background: var(--bg-secondary);
-      overflow-y: auto;
-      padding: 12px;
-      display: flex;
-      flex-direction: column;
-      gap: 12px;
-    }
-
-    .section-label {
-      font-size: 11px;
-      color: var(--text-secondary);
-      text-transform: uppercase;
-      letter-spacing: 0.5px;
-      margin-bottom: 8px;
-    }
-
-    /* Temperature Grid */
-    .temp-grid {
-      display: grid;
-      grid-template-columns: 1fr 1fr;
-      gap: 6px;
-    }
-
-    .temp-item {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 8px 10px;
-      background: var(--bg-card);
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .temp-item:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .temp-icon {
-      font-size: 14px;
-    }
-
-    .temp-value {
-      font-size: 16px;
-      font-weight: 500;
-    }
-
-    .temp-target {
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    /* Quick Buttons */
-    .quick-row {
-      display: flex;
-      gap: 6px;
-    }
-
-    .quick-btn {
-      flex: 1;
-      padding: 8px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 4px;
-    }
-
-    .quick-btn:hover {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    .quick-btn.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    /* Speed Control */
-    .speed-row {
-      display: flex;
-      gap: 4px;
-    }
-
-    .speed-btn {
-      flex: 1;
-      padding: 8px 4px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-    }
-
-    .speed-btn:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .speed-btn.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    .speed-btn.ludicrous {
-      color: var(--red);
-    }
-
-    .speed-btn.ludicrous.active {
-      background: var(--red);
-      color: white;
-    }
-
-    /* Movement Section */
-    .movement-row {
-      display: flex;
-      gap: 12px;
-      align-items: flex-start;
-    }
-
-    /* XY Jog Pad - Bambu Studio Style */
-    .jog-container {
-      position: relative;
-      width: 110px;
-      height: 110px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: var(--bg-card);
-      position: relative;
-      overflow: hidden;
-    }
-
-    .jog-quadrant {
-      position: absolute;
-      width: 50%;
-      height: 50%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      transition: background 0.15s;
-    }
-
-    .jog-quadrant:hover {
-      background: rgba(255,255,255,0.1);
-    }
-
-    .jog-quadrant.top { top: 0; left: 25%; width: 50%; height: 35%; }
-    .jog-quadrant.bottom { bottom: 0; left: 25%; width: 50%; height: 35%; }
-    .jog-quadrant.left { left: 0; top: 25%; width: 35%; height: 50%; }
-    .jog-quadrant.right { right: 0; top: 25%; width: 35%; height: 50%; }
-
-    .jog-arrow {
-      font-size: 16px;
-      color: var(--text-secondary);
-    }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover {
-      background: var(--accent-hover);
-    }
-
-    .jog-home svg {
-      width: 20px;
-      height: 20px;
-      fill: white;
-    }
-
-    /* Z Control */
-    .z-control {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .z-btn {
-      width: 36px;
-      height: 32px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 12px;
-    }
-
-    .z-btn:hover {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    .z-label {
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    /* Step Sizes */
-    .step-col {
-      display: flex;
-      flex-direction: column;
-      gap: 3px;
-    }
-
-    .step-btn {
-      padding: 6px 10px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 4px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 10px;
-    }
-
-    .step-btn:hover, .step-btn.active {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    /* Extruder Toggle */
-    .extruder-col {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .extruder-toggle {
-      display: flex;
-      background: var(--bg-card);
-      border-radius: 6px;
-      overflow: hidden;
-    }
-
-    .extruder-toggle button {
-      padding: 5px 12px;
-      border: none;
-      background: none;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-    }
-
-    .extruder-toggle button.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    /* Extruder Graphic */
-    .extruder-graphic {
-      width: 50px;
-      height: 70px;
-      position: relative;
-    }
-
-    .extruder-graphic svg {
-      width: 100%;
-      height: 100%;
-    }
-
-    .extruder-btns {
-      display: flex;
-      flex-direction: column;
-      gap: 2px;
-    }
-
-    .ext-btn {
-      width: 28px;
-      height: 22px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 4px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 10px;
-    }
-
-    .ext-btn:hover {
-      background: var(--bg-tertiary);
-    }
-
-    /* AMS Section */
-    .ams-section {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    /* Nozzle Assignment (dual nozzle printers) */
-    .nozzle-assignment {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 10px;
-      align-items: center;
-    }
-
-    .nozzle-group {
-      flex: 1;
-      display: flex;
-      align-items: center;
-      padding: 6px 10px;
-      background: var(--bg-card);
-      border-radius: 6px;
-      border: 2px solid transparent;
-    }
-
-    .nozzle-group.active {
-      border-color: var(--accent);
-    }
-
-    .nozzle-colors {
-      display: flex;
-      gap: 3px;
-    }
-
-    .nozzle-dot {
-      width: 14px;
-      height: 14px;
-      border-radius: 3px;
-      border: 1px solid rgba(255,255,255,0.2);
-    }
-
-    .nozzle-dot.empty {
-      background: var(--bg-tertiary);
-      border-style: dashed;
-    }
-
-    .nozzle-separator {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      color: var(--text-muted);
-      font-size: 12px;
-    }
-
-    .ams-tabs {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      gap: 3px;
-      padding: 5px 8px;
-      background: var(--bg-card);
-      border: 2px solid transparent;
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .ams-tab.active {
-      border-color: var(--accent);
-    }
-
-    .ams-tab-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(255,255,255,0.2);
-    }
-
-    .ams-tab-num {
-      font-size: 10px;
-      color: var(--text-secondary);
-      margin-left: 2px;
-    }
-
-    /* AMS Slots Container */
-    .ams-content {
-      background: var(--bg-card);
-      border-radius: 8px;
-      padding: 10px;
-    }
-
-    .ams-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 10px;
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-      justify-content: center;
-    }
-
-    /* AMS Slot - Bambu Studio Style */
-    .ams-slot {
-      width: 52px;
-      cursor: pointer;
-      border: 2px solid transparent;
-      border-radius: 8px;
-      overflow: hidden;
-      transition: all 0.15s;
-      background: var(--bg-secondary);
-    }
-
-    .ams-slot:hover {
-      border-color: var(--text-muted);
-    }
-
-    .ams-slot.active {
-      border-color: var(--accent);
-      box-shadow: 0 0 0 1px var(--accent);
-    }
-
-    .ams-slot.empty {
-      opacity: 0.4;
-    }
-
-    .ams-slot-color {
-      height: 38px;
-      position: relative;
-      display: flex;
-      align-items: flex-end;
-      justify-content: center;
-      padding-bottom: 4px;
-    }
-
-    .ams-slot-icon {
-      width: 14px;
-      height: 14px;
-      opacity: 0.8;
-    }
-
-    .ams-slot-info {
-      background: var(--bg-tertiary);
-      padding: 4px;
-      text-align: center;
-    }
-
-    .ams-slot-type {
-      font-size: 10px;
-      font-weight: 600;
-      color: var(--text-primary);
-    }
-
-    .ams-slot-num {
-      font-size: 8px;
-      color: var(--text-muted);
-    }
-
-    /* External Spool Row */
-    .ext-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-top: 10px;
-    }
-
-    .ext-label {
-      font-size: 11px;
-      color: var(--text-secondary);
-      width: 24px;
-    }
-
-    .ext-slot {
-      width: 52px;
-      height: 58px;
-      background: var(--bg-secondary);
-      border-radius: 8px;
-      border: 2px solid transparent;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      font-size: 16px;
-      color: var(--text-muted);
-    }
-
-    .ext-slot:hover {
-      border-color: var(--text-muted);
-    }
-
-    .ext-slot-label {
-      font-size: 8px;
-      margin-top: 2px;
-    }
-
-    /* Spool Holder Graphic */
-    .spool-holder {
-      flex: 1;
-      height: 58px;
-      background: var(--bg-secondary);
-      border-radius: 8px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      position: relative;
-    }
-
-    .spool-holder svg {
-      width: 80px;
-      height: 50px;
-    }
-
-    /* AMS Buttons */
-    .ams-buttons {
-      display: flex;
-      gap: 6px;
-      margin-top: 10px;
-    }
-
-    .ams-btn {
-      flex: 1;
-      padding: 10px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      font-weight: 500;
-    }
-
-    .ams-btn.secondary {
-      background: var(--bg-card);
-      color: var(--text-primary);
-    }
-
-    .ams-btn.secondary:hover {
-      background: var(--bg-tertiary);
-    }
-
-    .ams-btn.primary {
-      background: var(--accent);
-      color: white;
-    }
-
-    .ams-btn.primary:hover {
-      background: var(--accent-hover);
-    }
-
-    /* AMS Animation Area */
-    .ams-animation {
-      flex: 1;
-      min-height: 80px;
-      margin-top: 10px;
-      background: var(--bg-card);
-      border-radius: 8px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      gap: 8px;
-    }
-
-    .hub-graphic {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .hub-connector {
-      width: 30px;
-      height: 2px;
-      background: var(--text-muted);
-    }
-
-    .hub-box {
-      width: 50px;
-      height: 20px;
-      background: var(--bg-tertiary);
-      border-radius: 3px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 9px;
-      color: var(--text-muted);
-    }
-
-    .ams-status {
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-  </style>
-</head>
-<body>
-  <!-- Printer Tabs -->
-  <div class="printer-tabs">
-    <button class="printer-tab active">
-      <span class="status-dot"></span>
-      H2D-1
-      <span class="status-badge">IDLE</span>
-    </button>
-    <button class="printer-tab">
-      <span class="status-dot printing"></span>
-      X1C-2
-    </button>
-    <button class="printer-tab">
-      <span class="status-dot offline"></span>
-      P1S-3
-    </button>
-  </div>
-
-  <div class="main-content">
-    <!-- Left: Camera + Progress -->
-    <div class="left-column">
-      <div class="camera-section">
-        <div class="camera-controls">
-          <button class="cam-btn">▶ Start</button>
-          <button class="cam-btn">⛶ Fullscreen</button>
-        </div>
-        <span class="camera-placeholder">📷 Camera Feed</span>
-      </div>
-
-      <div class="print-progress">
-        <div class="print-thumb">No Print</div>
-        <div class="print-info">
-          <div class="print-name">Idle</div>
-          <div class="progress-bar"><div class="progress-fill"></div></div>
-          <div class="print-stats">
-            Layer: <span>--/--</span>
-            Time: <span>--:--</span>
-            Remaining: <span>--:--</span>
-          </div>
-        </div>
-        <div class="print-btns">
-          <button class="print-btn pause" disabled>⏸</button>
-          <button class="print-btn stop" disabled>⏹</button>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right: Control Panel -->
-    <div class="control-panel">
-      <!-- Temperature -->
-      <div>
-        <div class="section-label">Temperature</div>
-        <div class="temp-grid">
-          <div class="temp-item">
-            <span class="temp-icon">🔥</span>
-            <span style="font-size: 11px; color: var(--accent); margin-right: 2px;">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-          <div class="temp-item">
-            <span class="temp-icon">🔥</span>
-            <span style="font-size: 11px; color: var(--accent); margin-right: 2px;">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-          <div class="temp-item">
-            <span class="temp-icon">🛏️</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-          <div class="temp-item">
-            <span class="temp-icon">📦</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-        </div>
-        <div class="quick-row" style="margin-top: 8px;">
-          <button class="quick-btn">❄️ Fans</button>
-          <button class="quick-btn active">💡 Lamp</button>
-          <button class="quick-btn">🌀 Cool</button>
-        </div>
-      </div>
-
-      <!-- Speed -->
-      <div>
-        <div class="section-label">Print Speed</div>
-        <div class="speed-row">
-          <button class="speed-btn">Silent</button>
-          <button class="speed-btn active">Standard</button>
-          <button class="speed-btn">Sport</button>
-          <button class="speed-btn ludicrous">Ludicrous</button>
-        </div>
-      </div>
-
-      <!-- Movement -->
-      <div>
-        <div class="section-label">Movement</div>
-        <div class="movement-row">
-          <!-- XY Jog -->
-          <div class="jog-container">
-            <div class="jog-ring">
-              <div class="jog-quadrant top"><span class="jog-arrow">▲</span></div>
-              <div class="jog-quadrant bottom"><span class="jog-arrow">▼</span></div>
-              <div class="jog-quadrant left"><span class="jog-arrow">◀</span></div>
-              <div class="jog-quadrant right"><span class="jog-arrow">▶</span></div>
-              <button class="jog-home">
-                <svg viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>
-              </button>
-            </div>
-          </div>
-
-          <!-- Z -->
-          <div class="z-control">
-            <button class="z-btn">▲</button>
-            <span class="z-label">Z</span>
-            <button class="z-btn">▼</button>
-          </div>
-
-          <!-- Steps -->
-          <div class="step-col">
-            <button class="step-btn">10mm</button>
-            <button class="step-btn active">1mm</button>
-            <button class="step-btn">0.1mm</button>
-          </div>
-
-          <!-- Extruder -->
-          <div class="extruder-col">
-            <div class="extruder-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic">
-              <svg viewBox="0 0 60 80">
-                <!-- Filament tubes -->
-                <path d="M20 0 L20 25 Q20 30 25 35 L25 50" stroke="#00ae42" stroke-width="3" fill="none"/>
-                <path d="M40 0 L40 25 Q40 30 35 35 L35 50" stroke="#00ae42" stroke-width="3" fill="none"/>
-                <!-- Extruder body -->
-                <rect x="10" y="45" width="40" height="30" rx="4" fill="#4a4a4a"/>
-                <!-- Nozzles -->
-                <rect x="18" y="75" width="8" height="5" fill="#666"/>
-                <rect x="34" y="75" width="8" height="5" fill="#666"/>
-                <!-- Gears -->
-                <circle cx="20" cy="55" r="6" fill="#555"/>
-                <circle cx="40" cy="55" r="6" fill="#555"/>
-              </svg>
-            </div>
-            <div class="extruder-btns">
-              <button class="ext-btn">▲</button>
-              <button class="ext-btn">▼</button>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <!-- AMS -->
-      <div class="ams-section">
-        <div class="section-label">AMS</div>
-
-        <!-- Nozzle Assignment Row (for dual nozzle printers) -->
-        <div class="nozzle-assignment">
-          <div class="nozzle-group">
-            <div class="nozzle-colors">
-              <div class="nozzle-dot" style="background: #FFD700;"></div>
-              <div class="nozzle-dot" style="background: #8B4513;"></div>
-              <div class="nozzle-dot" style="background: #1a1a1a;"></div>
-              <div class="nozzle-dot" style="background: #222;"></div>
-            </div>
-          </div>
-          <div class="nozzle-separator">
-            <div class="nozzle-dot" style="background: #0066FF;"></div>
-            <span>/</span>
-            <div class="nozzle-dot" style="background: #FF0000;"></div>
-          </div>
-          <div class="nozzle-group right">
-            <div class="nozzle-colors">
-              <div class="nozzle-dot empty"></div>
-            </div>
-          </div>
-        </div>
-
-        <div class="ams-tabs">
-          <div class="ams-tab active">
-            <div class="ams-tab-dot" style="background: #FFD700;"></div>
-            <div class="ams-tab-dot" style="background: #1a1a1a;"></div>
-            <div class="ams-tab-dot" style="background: #8B4513;"></div>
-            <div class="ams-tab-dot" style="background: #222;"></div>
-            <span class="ams-tab-num">1</span>
-          </div>
-          <div class="ams-tab">
-            <div class="ams-tab-dot" style="background: #FF0000;"></div>
-            <div class="ams-tab-dot" style="background: #0066FF;"></div>
-            <div class="ams-tab-dot" style="background: #3d3d3d;"></div>
-            <div class="ams-tab-dot" style="background: #3d3d3d;"></div>
-            <span class="ams-tab-num">2</span>
-          </div>
-          <div class="ams-tab">
-            <div class="ams-tab-dot" style="background: #00FF00;"></div>
-            <div class="ams-tab-dot" style="background: #FFF;"></div>
-            <div class="ams-tab-dot" style="background: #FF69B4;"></div>
-            <div class="ams-tab-dot" style="background: #9932CC;"></div>
-            <span class="ams-tab-num">3</span>
-          </div>
-        </div>
-
-        <div class="ams-content">
-          <div class="ams-header">
-            <span>AMS 1</span>
-            <span>💧 18% · 🌡 24°C</span>
-          </div>
-          <div class="ams-slots">
-            <div class="ams-slot active">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #FFD700 0%, #E6C200 100%);">
-                <svg class="ams-slot-icon" viewBox="0 0 24 24" fill="rgba(0,0,0,0.5)">
-                  <circle cx="12" cy="12" r="8" stroke-width="2" fill="none" stroke="currentColor"/>
-                  <circle cx="12" cy="12" r="3"/>
-                </svg>
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PLA</div>
-                <div class="ams-slot-num">A1</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%);">
-                <svg class="ams-slot-icon" viewBox="0 0 24 24" fill="rgba(255,255,255,0.5)">
-                  <circle cx="12" cy="12" r="8" stroke-width="2" fill="none" stroke="currentColor"/>
-                  <circle cx="12" cy="12" r="3"/>
-                </svg>
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PETG</div>
-                <div class="ams-slot-num">A2</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #8B4513 0%, #6B3510 100%);">
-                <svg class="ams-slot-icon" viewBox="0 0 24 24" fill="rgba(255,255,255,0.5)">
-                  <circle cx="12" cy="12" r="8" stroke-width="2" fill="none" stroke="currentColor"/>
-                  <circle cx="12" cy="12" r="3"/>
-                </svg>
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PETG</div>
-                <div class="ams-slot-num">A3</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #222 0%, #111 100%);">
-                <svg class="ams-slot-icon" viewBox="0 0 24 24" fill="rgba(255,255,255,0.5)">
-                  <circle cx="12" cy="12" r="8" stroke-width="2" fill="none" stroke="currentColor"/>
-                  <circle cx="12" cy="12" r="3"/>
-                </svg>
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PLA</div>
-                <div class="ams-slot-num">A4</div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- External -->
-        <div class="ext-row">
-          <span class="ext-label">Ext</span>
-          <div class="ext-slot">
-            ?
-            <span class="ext-slot-label">Spool</span>
-          </div>
-          <div class="spool-holder">
-            <svg viewBox="0 0 100 60">
-              <!-- Spool holder base -->
-              <rect x="10" y="40" width="80" height="15" rx="3" fill="#4a4a4a"/>
-              <!-- Spool -->
-              <ellipse cx="50" cy="30" rx="25" ry="25" fill="#3a3a3a" stroke="#555" stroke-width="2"/>
-              <ellipse cx="50" cy="30" rx="10" ry="10" fill="#2a2a2a"/>
-              <ellipse cx="50" cy="30" rx="5" ry="5" fill="#00ae42"/>
-              <!-- Filament coming out -->
-              <path d="M75 30 Q85 30 90 25" stroke="#00ae42" stroke-width="2" fill="none"/>
-            </svg>
-          </div>
-        </div>
-
-        <!-- Buttons -->
-        <div class="ams-buttons">
-          <button class="ams-btn secondary">Unload</button>
-          <button class="ams-btn primary">Load</button>
-        </div>
-
-        <!-- Animation Area -->
-        <div class="ams-animation">
-          <div class="hub-graphic">
-            <div class="hub-connector"></div>
-            <div class="hub-box">HUB</div>
-            <div class="hub-connector"></div>
-          </div>
-          <div class="ams-status">Ready - Select a slot to load</div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1131
mockup/control-page-v20.html

@@ -1,1131 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v20</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 640px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 16px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row - compact */
-    .ams-tab-row {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 5px 6px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 5px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 3px 5px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 28px;
-      height: 22px;
-      object-fit: contain;
-    }
-
-    .ams-tab.ams-ht-tab .ht-dot {
-      position: absolute;
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      bottom: 4px;
-      left: 50%;
-      transform: translateX(-50%);
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 12px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 16px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 16px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots - thinner */
-    .ams-slots {
-      display: flex;
-      gap: 5px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 60px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Slot Connectors - vertical lines from each slot */
-    .slot-connectors {
-      display: flex;
-      padding: 0 8px;
-      position: relative;
-      height: 16px;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 16px;
-      background: #c0c0c0;
-    }
-
-    /* Horizontal bar connecting all vertical lines */
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 10%;
-      right: 10%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Hub rectangle centered under slots */
-    .hub-container {
-      display: flex;
-      justify-content: center;
-      position: relative;
-    }
-
-    .hub-rect {
-      width: 40px;
-      height: 14px;
-      background: #d0d0d0;
-      border-radius: 3px;
-      border: 1px solid #bbb;
-    }
-
-    /* Line from hub going down to extruder area */
-    .hub-line-down {
-      display: flex;
-      justify-content: center;
-      height: 25px;
-    }
-
-    .hub-line-down .vline {
-      width: 2px;
-      height: 100%;
-      background: #c0c0c0;
-    }
-
-    /* Extruder row with wiring */
-    .extruder-row {
-      display: flex;
-      align-items: flex-start;
-      justify-content: center;
-      position: relative;
-      margin-top: 8px;
-    }
-
-    /* Horizontal line connecting both hub lines to extruder */
-    .extruder-hbar {
-      position: absolute;
-      top: 0;
-      left: 25%;
-      right: 25%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Extruder image centered */
-    .extruder-center {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      position: relative;
-    }
-
-    .extruder-center .vline-to-extruder {
-      width: 2px;
-      height: 8px;
-      background: #c0c0c0;
-    }
-
-    .extruder-center img {
-      height: 50px;
-    }
-
-    /* AMS Actions - left aligned group, center extruder placeholder, right aligned group */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-top: 12px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-actions-left {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-actions-center {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .ams-actions-center img {
-      height: 45px;
-    }
-
-    .ams-actions-right {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-
-                <div class="hub-container">
-                  <div class="hub-rect"></div>
-                </div>
-
-                <div class="hub-line-down">
-                  <div class="vline"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors: 4x AMS + 1x AMS-HT -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1↓</div>
-                  <div class="slot-label">B2↓</div>
-                  <div class="slot-label">B3↓</div>
-                  <div class="slot-label">B4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="slot-connectors">
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-line"><div class="vline"></div></div>
-                  <div class="connector-hbar"></div>
-                </div>
-
-                <div class="hub-container">
-                  <div class="hub-rect"></div>
-                </div>
-
-                <div class="hub-line-down">
-                  <div class="vline"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Extruder centered between both panels -->
-          <div class="extruder-row">
-            <div class="extruder-hbar"></div>
-            <div class="extruder-center">
-              <div class="vline-to-extruder"></div>
-              <img src="icons/extruder-left-right.png">
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <div class="ams-actions-left">
-              <button class="auto-refill-btn">Auto-refill</button>
-              <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            </div>
-            <div class="ams-actions-center">
-              <!-- spacer -->
-            </div>
-            <div class="ams-actions-right">
-              <button class="ams-action-btn">Unload</button>
-              <button class="ams-action-btn">Load</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1108
mockup/control-page-v21.html

@@ -1,1108 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v21</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 720px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 12px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      min-width: 0;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row - compact */
-    .ams-tab-row {
-      display: flex;
-      gap: 3px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 4px 5px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 2px 4px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 24px;
-      height: 18px;
-      object-fit: contain;
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 10px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 5px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 9px;
-      color: var(--text-muted);
-      padding: 2px 0;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-    }
-
-    /* AMS Slots - taller */
-    .ams-slots {
-      display: flex;
-      gap: 4px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 80px;
-      border-radius: 6px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Wiring container */
-    .wiring-container {
-      position: relative;
-      height: 50px;
-      margin-top: 4px;
-    }
-
-    /* Vertical lines from slots */
-    .slot-vlines {
-      display: flex;
-      padding: 0 4px;
-      height: 20px;
-    }
-
-    .slot-vline {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .slot-vline .vline {
-      width: 2px;
-      height: 20px;
-      background: #c0c0c0;
-    }
-
-    /* Horizontal bar */
-    .slot-hbar {
-      position: absolute;
-      top: 18px;
-      left: 12%;
-      right: 12%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Hub */
-    .hub-wrapper {
-      position: absolute;
-      top: 16px;
-      left: 50%;
-      transform: translateX(-50%);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .hub-rect {
-      width: 36px;
-      height: 12px;
-      background: #d0d0d0;
-      border: 1px solid #bbb;
-      border-radius: 2px;
-    }
-
-    .hub-vline {
-      width: 2px;
-      height: 20px;
-      background: #c0c0c0;
-    }
-
-    /* Connecting wiring between panels */
-    .connecting-wiring {
-      position: relative;
-      height: 25px;
-      margin-top: -8px;
-    }
-
-    .connecting-hbar {
-      position: absolute;
-      top: 0;
-      left: 25%;
-      right: 25%;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    .connecting-vline {
-      position: absolute;
-      top: 0;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 25px;
-      background: #c0c0c0;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-top: 12px;
-      padding-top: 14px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-actions-left {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-actions-center {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .ams-actions-center img {
-      height: 45px;
-    }
-
-    .ams-actions-right {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1↓</div>
-                  <div class="slot-label">A2↓</div>
-                  <div class="slot-label">A3↓</div>
-                  <div class="slot-label">A4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-container">
-                  <div class="slot-vlines">
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                  </div>
-                  <div class="slot-hbar"></div>
-                  <div class="hub-wrapper">
-                    <div class="hub-rect"></div>
-                    <div class="hub-vline"></div>
-                  </div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1↓</div>
-                  <div class="slot-label">B2↓</div>
-                  <div class="slot-label">B3↓</div>
-                  <div class="slot-label">B4↓</div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-container">
-                  <div class="slot-vlines">
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                  </div>
-                  <div class="slot-hbar"></div>
-                  <div class="hub-wrapper">
-                    <div class="hub-rect"></div>
-                    <div class="hub-vline"></div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Connecting wiring between panels -->
-          <div class="connecting-wiring">
-            <div class="connecting-hbar"></div>
-            <div class="connecting-vline"></div>
-          </div>
-
-          <!-- AMS Actions: [Auto-refill][Settings] ... [Extruder] ... [Unload][Load] -->
-          <div class="ams-actions">
-            <div class="ams-actions-left">
-              <button class="auto-refill-btn">Auto-refill</button>
-              <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            </div>
-            <div class="ams-actions-center">
-              <img src="icons/extruder-left-right.png">
-            </div>
-            <div class="ams-actions-right">
-              <button class="ams-action-btn">Unload</button>
-              <button class="ams-action-btn">Load</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1119
mockup/control-page-v22.html

@@ -1,1119 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v22</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 720px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 12px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      min-width: 0;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row - compact */
-    .ams-tab-row {
-      display: flex;
-      gap: 3px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 4px 5px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 2px 4px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 24px;
-      height: 18px;
-      object-fit: contain;
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 10px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 5px;
-    }
-
-    .slot-label {
-      flex: 1;
-      max-width: 60px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 3px;
-      font-size: 9px;
-      color: var(--text-muted);
-      padding: 2px 4px;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-    }
-
-    .slot-label img {
-      width: 10px;
-      height: 10px;
-      opacity: 0.5;
-    }
-
-    /* AMS Slots - narrower */
-    .ams-slots {
-      display: flex;
-      gap: 4px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      max-width: 60px;
-      height: 80px;
-      border-radius: 6px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* Wiring container */
-    .wiring-container {
-      position: relative;
-      height: 45px;
-      margin-top: 4px;
-    }
-
-    /* Vertical lines from slots */
-    .slot-vlines {
-      display: flex;
-      gap: 4px;
-      height: 18px;
-    }
-
-    .slot-vline {
-      flex: 1;
-      max-width: 60px;
-      display: flex;
-      justify-content: center;
-    }
-
-    .slot-vline .vline {
-      width: 2px;
-      height: 18px;
-      background: #c0c0c0;
-    }
-
-    /* Horizontal bar connecting slots */
-    .slot-hbar {
-      position: absolute;
-      top: 16px;
-      left: 30px;
-      right: 30px;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Hub */
-    .hub-wrapper {
-      position: absolute;
-      top: 14px;
-      left: 50%;
-      transform: translateX(-50%);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .hub-rect {
-      width: 36px;
-      height: 12px;
-      background: #d0d0d0;
-      border: 1px solid #bbb;
-      border-radius: 2px;
-    }
-
-    .hub-vline {
-      width: 2px;
-      height: 18px;
-      background: #c0c0c0;
-    }
-
-    /* Connecting wiring between panels to extruder */
-    .connecting-wiring {
-      position: relative;
-      height: 30px;
-      margin-top: -5px;
-    }
-
-    .connecting-hbar {
-      position: absolute;
-      top: 0;
-      left: calc(25% + 6px);
-      right: calc(25% + 6px);
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    .connecting-vline {
-      position: absolute;
-      top: 0;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 30px;
-      background: #c0c0c0;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      margin-top: 5px;
-      padding-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-actions-left {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-actions-center {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .ams-actions-center img {
-      height: 45px;
-    }
-
-    .ams-actions-right {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-container">
-                  <div class="slot-vlines">
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                  </div>
-                  <div class="slot-hbar"></div>
-                  <div class="hub-wrapper">
-                    <div class="hub-rect"></div>
-                    <div class="hub-vline"></div>
-                  </div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-container">
-                  <div class="slot-vlines">
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                    <div class="slot-vline"><div class="vline"></div></div>
-                  </div>
-                  <div class="slot-hbar"></div>
-                  <div class="hub-wrapper">
-                    <div class="hub-rect"></div>
-                    <div class="hub-vline"></div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Connecting wiring between panels -->
-          <div class="connecting-wiring">
-            <div class="connecting-hbar"></div>
-            <div class="connecting-vline"></div>
-          </div>
-
-          <!-- AMS Actions: [Auto-refill][Settings] ... [Extruder] ... [Unload][Load] -->
-          <div class="ams-actions">
-            <div class="ams-actions-left">
-              <button class="auto-refill-btn">Auto-refill</button>
-              <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            </div>
-            <div class="ams-actions-center">
-              <img src="icons/extruder-left-right.png">
-            </div>
-            <div class="ams-actions-right">
-              <button class="ams-action-btn">Unload</button>
-              <button class="ams-action-btn">Load</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1127
mockup/control-page-v23.html

@@ -1,1127 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v23</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 720px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 20px;
-      position: relative;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      min-width: 0;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row */
-    .ams-tab-row {
-      display: flex;
-      gap: 3px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 4px 5px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 2px 4px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 24px;
-      height: 18px;
-      object-fit: contain;
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 10px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-      margin-bottom: 5px;
-    }
-
-    .slot-label {
-      width: 48px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 2px;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 6px;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-    }
-
-    .slot-label img {
-      width: 12px;
-      height: 12px;
-      opacity: 0.5;
-    }
-
-    /* AMS Slots - narrow and tall */
-    .ams-slots {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-    }
-
-    .ams-slot {
-      width: 48px;
-      height: 70px;
-      border-radius: 6px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* ========== WIRING ========== */
-    .wiring-area {
-      position: relative;
-      height: 70px;
-      margin-top: 8px;
-    }
-
-    /* Vertical lines from slots */
-    .slot-wires {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-    }
-
-    .slot-wire {
-      width: 48px;
-      display: flex;
-      justify-content: center;
-    }
-
-    .slot-wire .vline {
-      width: 2px;
-      height: 20px;
-      background: #c8c8c8;
-    }
-
-    /* Horizontal bar */
-    .wire-hbar {
-      position: absolute;
-      top: 18px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 180px;
-      height: 2px;
-      background: #c8c8c8;
-    }
-
-    /* Hub rectangle ON the horizontal bar */
-    .hub-box {
-      position: absolute;
-      top: 12px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 32px;
-      height: 14px;
-      background: #e0e0e0;
-      border: 1px solid #ccc;
-      border-radius: 2px;
-    }
-
-    /* Line from hub going down toward extruder */
-    .hub-down-line {
-      position: absolute;
-      top: 26px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 44px;
-      background: #c8c8c8;
-    }
-
-    /* Center extruder area - positioned at convergence */
-    .center-extruder-area {
-      display: flex;
-      justify-content: center;
-      align-items: flex-start;
-      position: relative;
-      height: 60px;
-      margin-top: -15px;
-    }
-
-    /* Horizontal bar connecting both sides */
-    .center-hbar {
-      position: absolute;
-      top: 0;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 300px;
-      height: 2px;
-      background: #c8c8c8;
-    }
-
-    /* Extruder at convergence point */
-    .center-extruder {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      margin-top: -2px;
-    }
-
-    .center-extruder .vline {
-      width: 2px;
-      height: 12px;
-      background: #c8c8c8;
-    }
-
-    .center-extruder img {
-      height: 45px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      margin-top: 8px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-actions-left {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-actions-center {
-      flex: 1;
-    }
-
-    .ams-actions-right {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-area">
-                  <div class="slot-wires">
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                  </div>
-                  <div class="wire-hbar"></div>
-                  <div class="hub-box"></div>
-                  <div class="hub-down-line"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-area">
-                  <div class="slot-wires">
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                  </div>
-                  <div class="wire-hbar"></div>
-                  <div class="hub-box"></div>
-                  <div class="hub-down-line"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Center extruder at convergence of both wiring paths -->
-          <div class="center-extruder-area">
-            <div class="center-hbar"></div>
-            <div class="center-extruder">
-              <div class="vline"></div>
-              <img src="icons/extruder-left-right.png">
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <div class="ams-actions-left">
-              <button class="auto-refill-btn">Auto-refill</button>
-              <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            </div>
-            <div class="ams-actions-center"></div>
-            <div class="ams-actions-right">
-              <button class="ams-action-btn">Unload</button>
-              <button class="ams-action-btn">Load</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1136
mockup/control-page-v24.html

@@ -1,1136 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v24</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-      --wire-color: #c8c8c8;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 720px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-    }
-
-    .camera-overlay img {
-      width: 24px;
-      height: 24px;
-      opacity: 0.7;
-      filter: invert(1);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 16px 20px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 30px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 160px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 8px;
-      border-radius: 4px;
-      min-width: 24px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 24px;
-      padding: 2px 8px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 15px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 0;
-      margin-top: 10px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 18px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      margin-top: 8px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 16px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: flex-end;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 12px;
-    }
-
-    .nozzle-toggle button {
-      padding: 7px 20px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 24px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-      margin-bottom: 12px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 40px;
-      height: 40px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 20px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 12px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 80px;
-      margin: 8px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 16px;
-      margin-bottom: 12px;
-      position: relative;
-    }
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 20px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      min-width: 0;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row */
-    .ams-tab-row {
-      display: flex;
-      gap: 3px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 4px 5px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 2px 4px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 24px;
-      height: 18px;
-      object-fit: contain;
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 10px;
-      position: relative;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-      margin-bottom: 5px;
-    }
-
-    .slot-label {
-      width: 48px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 2px;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 6px;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-    }
-
-    .slot-label img {
-      width: 12px;
-      height: 12px;
-      opacity: 0.5;
-    }
-
-    /* AMS Slots - narrow and tall */
-    .ams-slots {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-    }
-
-    .ams-slot {
-      width: 48px;
-      height: 70px;
-      border-radius: 6px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 9px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 3px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* ========== WIRING ========== */
-    .wiring-area {
-      position: relative;
-      height: 55px;
-      margin-top: 8px;
-    }
-
-    /* Vertical lines from slots */
-    .slot-wires {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-    }
-
-    .slot-wire {
-      width: 48px;
-      display: flex;
-      justify-content: center;
-    }
-
-    .slot-wire .vline {
-      width: 2px;
-      height: 16px;
-      background: var(--wire-color);
-    }
-
-    /* Horizontal bar connecting slot wires */
-    .wire-hbar {
-      position: absolute;
-      top: 14px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 180px;
-      height: 2px;
-      background: var(--wire-color);
-    }
-
-    /* Hub rectangle ON the horizontal bar */
-    .hub-box {
-      position: absolute;
-      top: 9px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 28px;
-      height: 12px;
-      background: #e8e8e8;
-      border: 1px solid #d0d0d0;
-      border-radius: 2px;
-    }
-
-    /* Vertical line from hub going down */
-    .hub-vline {
-      position: absolute;
-      top: 21px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 34px;
-      background: var(--wire-color);
-    }
-
-    /* L-bend for left panel (goes right toward center) */
-    .ams-panel.left-panel .wire-bend {
-      position: absolute;
-      bottom: 0;
-      left: 50%;
-      width: calc(50% + 10px);
-      height: 2px;
-      background: var(--wire-color);
-    }
-
-    /* L-bend for right panel (goes left toward center) */
-    .ams-panel.right-panel .wire-bend {
-      position: absolute;
-      bottom: 0;
-      right: 50%;
-      width: calc(50% + 10px);
-      height: 2px;
-      background: var(--wire-color);
-    }
-
-    /* Center extruder area */
-    .center-extruder-area {
-      display: flex;
-      justify-content: center;
-      position: relative;
-      height: 55px;
-      margin-top: -5px;
-    }
-
-    .center-extruder {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .center-extruder .vline {
-      width: 2px;
-      height: 8px;
-      background: var(--wire-color);
-    }
-
-    .center-extruder img {
-      height: 45px;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      margin-top: 8px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-actions-left {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-actions-center {
-      flex: 1;
-    }
-
-    .ams-actions-right {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        <div class="camera-overlay">
-          <img src="icons/micro-sd.svg">
-        </div>
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel left-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-area">
-                  <div class="slot-wires">
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                  </div>
-                  <div class="wire-hbar"></div>
-                  <div class="hub-box"></div>
-                  <div class="hub-vline"></div>
-                  <div class="wire-bend"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel right-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring -->
-                <div class="wiring-area">
-                  <div class="slot-wires">
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                  </div>
-                  <div class="wire-hbar"></div>
-                  <div class="hub-box"></div>
-                  <div class="hub-vline"></div>
-                  <div class="wire-bend"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Center extruder at convergence of both wiring paths -->
-          <div class="center-extruder-area">
-            <div class="center-extruder">
-              <div class="vline"></div>
-              <img src="icons/extruder-left-right.png">
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <div class="ams-actions-left">
-              <button class="auto-refill-btn">Auto-refill</button>
-              <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            </div>
-            <div class="ams-actions-center"></div>
-            <div class="ams-actions-right">
-              <button class="ams-action-btn">Unload</button>
-              <button class="ams-action-btn">Load</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1083
mockup/control-page-v25.html

@@ -1,1083 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v25</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f8f8f8;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-      --wire-color: #c8c8c8;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 620px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      display: none;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-      position: relative;
-    }
-
-    .camera-overlay {
-      display: none;
-    }
-
-    .sd-card-icon {
-      width: 18px;
-      height: 18px;
-      opacity: 0.6;
-      transform: rotate(-90deg);
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 16px; align-items: center; }
-    .progress-thumb {
-      width: 80px; height: 80px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 11px;
-      color: var(--text-muted);
-      overflow: hidden;
-    }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      overflow-x: hidden;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 16px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 7px 16px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 12px 16px;
-      flex: 1;
-      overflow-y: auto;
-      overflow-x: hidden;
-    }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 24px;
-      margin-bottom: 16px;
-      min-height: 300px;
-      align-items: stretch;
-    }
-
-    /* Temperature Column */
-    .temp-column {
-      flex: 0 0 auto;
-      min-width: 150px;
-      padding-right: 20px;
-      border-right: 1px solid var(--border-light);
-      display: flex;
-      flex-direction: column;
-      justify-content: space-evenly;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .temp-icon { width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 20px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 6px;
-      border-radius: 3px;
-      min-width: 18px;
-      text-align: center;
-    }
-
-    .temp-badge-spacer {
-      min-width: 30px;
-    }
-
-    .temp-value { font-size: 18px; font-weight: 500; }
-    .temp-target { font-size: 14px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 20px; opacity: 0.5; }
-
-    .air-values {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-    }
-
-    .air-value {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .air-value img { width: 18px; opacity: 0.5; }
-
-    .lamp-section {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .lamp-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      gap: 24px;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-    }
-
-    .nozzle-toggle button {
-      padding: 6px 18px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 13px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .jog-pad {
-      position: relative;
-      width: 220px;
-      height: 220px;
-      margin-bottom: 14px;
-    }
-
-    .jog-pad img {
-      width: 100%;
-      height: 100%;
-    }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .bed-btn {
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 13px; color: var(--text-muted); padding: 0 8px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-      flex: 1;
-      justify-content: center;
-    }
-
-    .extruder-section .nozzle-toggle {
-      margin-bottom: 4px;
-    }
-
-    .extruder-btn {
-      width: 36px;
-      height: 30px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 120px;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      background: var(--bg-panel);
-      border-radius: 10px;
-      padding: 12px;
-      margin-bottom: 12px;
-      position: relative;
-    }
-
-    /* Center vertical wire - removed, using center-wiring instead */
-
-    /* Dual nozzle layout */
-    .ams-dual-layout {
-      display: flex;
-      gap: 20px;
-    }
-
-    .ams-panel {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      min-width: 0;
-    }
-
-    .ams-panel-label {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-muted);
-      text-transform: uppercase;
-      margin-bottom: 8px;
-      text-align: center;
-    }
-
-    /* AMS Tab Selector Row */
-    .ams-tab-row {
-      display: flex;
-      gap: 3px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      padding: 4px 5px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { border-color: #ccc; }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab .color-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab .color-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .ams-tab .color-dot.empty {
-      background: #e0e0e0;
-    }
-
-    /* AMS-HT Tab */
-    .ams-tab.ams-ht-tab {
-      padding: 2px 4px;
-    }
-
-    .ams-tab.ams-ht-tab img {
-      width: 24px;
-      height: 18px;
-      object-fit: contain;
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-white);
-      border-radius: 10px;
-      padding: 10px;
-      padding-bottom: 0;
-      position: relative;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 8px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-stat {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-stat img { width: 14px; opacity: 0.5; }
-    .ams-stat .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-      margin-bottom: 5px;
-    }
-
-    .slot-label {
-      width: 48px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 2px;
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 3px 6px;
-      background: var(--bg-panel);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-    }
-
-    .slot-label img {
-      width: 10px;
-      height: 10px;
-      opacity: 0.7;
-    }
-
-    /* AMS Slots - narrow and tall */
-    .ams-slots {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-    }
-
-    .ams-slot {
-      width: 48px;
-      height: 70px;
-      border-radius: 6px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 5px;
-    }
-
-    .ams-slot-type {
-      font-size: 11px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 4px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 14px; height: 14px; }
-    .ams-slot-eye img { width: 14px; height: 14px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    /* ========== WIRING (per panel) ========== */
-    .wiring-area {
-      position: relative;
-      height: 40px;
-      margin-top: 6px;
-      overflow: visible;
-    }
-
-    /* Vertical lines from slots */
-    .slot-wires {
-      display: flex;
-      justify-content: center;
-      gap: 6px;
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-    }
-
-    .slot-wire {
-      width: 48px;
-      display: flex;
-      justify-content: center;
-    }
-
-    .slot-wire .vline {
-      width: 2px;
-      height: 14px;
-      background: var(--wire-color);
-    }
-
-    /* Horizontal bar connecting slot wires */
-    .wire-hbar {
-      position: absolute;
-      top: 12px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 180px;
-      height: 2px;
-      background: var(--wire-color);
-    }
-
-    /* Hub rectangle ON the horizontal bar */
-    .hub-box {
-      position: absolute;
-      top: 7px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 28px;
-      height: 12px;
-      background: #e8e8e8;
-      border: 1px solid #d0d0d0;
-      border-radius: 2px;
-    }
-
-    /* Vertical line from hub going down */
-    .hub-vline {
-      position: absolute;
-      top: 19px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 16px;
-      background: var(--wire-color);
-    }
-
-    /* Horizontal bend - extends from hub toward center */
-    .ams-panel:first-child .wire-bend {
-      position: absolute;
-      top: 33px;
-      left: 50%;
-      width: calc(50% + 40px);
-      height: 2px;
-      background: var(--wire-color);
-    }
-
-    .ams-panel:last-child .wire-bend {
-      position: absolute;
-      top: 33px;
-      right: 50%;
-      width: calc(50% + 40px);
-      height: 2px;
-      background: var(--wire-color);
-    }
-
-    /* ========== CENTER WIRING ========== */
-    .center-wiring {
-      position: relative;
-      height: 0;
-      margin-top: -7px;
-      z-index: 10;
-    }
-
-    .wire-center-hbar {
-      display: none;
-    }
-
-    .wire-center-down {
-      position: absolute;
-      top: 0;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 2px;
-      height: 68px;
-      background: var(--wire-color);
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      margin-top: 0;
-      padding-top: 8px;
-    }
-
-    .ams-actions-left {
-      flex: 1;
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-actions-center {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-start;
-    }
-
-    .ams-actions-center .wire-to-extruder {
-      width: 2px;
-      height: 20px;
-      background: var(--wire-color);
-    }
-
-    .ams-actions-center img {
-      height: 45px;
-    }
-
-    .ams-actions-right {
-      flex: 1;
-      display: flex;
-      align-items: center;
-      justify-content: flex-end;
-      gap: 8px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 18px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 40px;
-      height: 40px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 22px; opacity: 0.5; }
-
-    .ams-action-btn {
-      padding: 10px 28px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <div style="flex: 1;"></div>
-        <button class="icon-btn"><img class="sd-card-icon" src="icons/micro-sd.svg"></button>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-      </div>
-      <div class="camera-feed">
-        Camera Feed
-      </div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-badge-spacer"></span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div class="air-values">
-              <div class="air-value"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-section">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="jog-section">
-              <div class="jog-pad">
-                <img src="icons/jogpad.svg" alt="Jog Pad">
-              </div>
-              <div class="bed-controls">
-                <button class="bed-btn">&#8593;10</button>
-                <button class="bed-btn">&#8593;1</button>
-                <span class="bed-label">Bed</span>
-                <button class="bed-btn">&#8595;1</button>
-                <button class="bed-btn">&#8595;10</button>
-              </div>
-            </div>
-
-            <div class="extruder-section">
-              <div class="nozzle-toggle">
-                <button class="active">Left</button>
-                <button>Right</button>
-              </div>
-              <button class="extruder-btn">&#9650;</button>
-              <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-              <button class="extruder-btn">&#9660;</button>
-              <span class="extruder-label">Extruder</span>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <div class="ams-dual-layout">
-            <!-- LEFT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Left Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFD700;"></div>
-                    <div class="color-dot" style="background: #333;"></div>
-                    <div class="color-dot" style="background: #8B4513;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FF6600;"></div>
-                    <div class="color-dot" style="background: #00AA00;"></div>
-                    <div class="color-dot" style="background: #0066FF;"></div>
-                    <div class="color-dot" style="background: #FF0000;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 18 %</div>
-                  <div class="ams-stat"><img src="icons/temperature.svg"> 24°C</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">A1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">A4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #333;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring (per panel) -->
-                <div class="wiring-area">
-                  <div class="slot-wires">
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                  </div>
-                  <div class="wire-hbar"></div>
-                  <div class="hub-box"></div>
-                  <div class="hub-vline"></div>
-                  <div class="wire-bend"></div>
-                </div>
-              </div>
-            </div>
-
-            <!-- RIGHT NOZZLE PANEL -->
-            <div class="ams-panel">
-              <div class="ams-panel-label">Right Nozzle</div>
-
-              <!-- AMS Tab Selectors -->
-              <div class="ams-tab-row">
-                <div class="ams-tab active">
-                  <div class="color-dots">
-                    <div class="color-dot" style="background: #FFFFFF; border-color: #999;"></div>
-                    <div class="color-dot" style="background: #222;"></div>
-                    <div class="color-dot" style="background: #666;"></div>
-                    <div class="color-dot" style="background: #999;"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab">
-                  <div class="color-dots">
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                    <div class="color-dot empty"></div>
-                  </div>
-                </div>
-                <div class="ams-tab ams-ht-tab">
-                  <img src="icons/ams-ht.png">
-                </div>
-              </div>
-
-              <!-- AMS Content -->
-              <div class="ams-content">
-                <div class="ams-header">
-                  <div class="ams-stat"><img src="icons/water.svg"> 14 %</div>
-                  <div class="ams-stat"><img src="icons/temperature.svg"> 23°C</div>
-                  <div class="ams-stat"><span class="sun">&#9788;</span></div>
-                </div>
-
-                <div class="slot-labels">
-                  <div class="slot-label">B1 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B2 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B3 <img src="icons/reload.svg"></div>
-                  <div class="slot-label">B4 <img src="icons/reload.svg"></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #FFFFFF;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #666;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #999;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Wiring (per panel) -->
-                <div class="wiring-area">
-                  <div class="slot-wires">
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                    <div class="slot-wire"><div class="vline"></div></div>
-                  </div>
-                  <div class="wire-hbar"></div>
-                  <div class="hub-box"></div>
-                  <div class="hub-vline"></div>
-                  <div class="wire-bend"></div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- Center wiring connecting both panels to extruder -->
-          <div class="center-wiring">
-            <div class="wire-center-hbar"></div>
-            <div class="wire-center-down"></div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <div class="ams-actions-left">
-              <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-              <button class="auto-refill-btn">Auto-refill</button>
-            </div>
-            <div class="ams-actions-center">
-              <div class="wire-to-extruder"></div>
-              <img src="icons/extruder-left-right.png">
-            </div>
-            <div class="ams-actions-right">
-              <button class="ams-action-btn">Unload</button>
-              <button class="ams-action-btn">Load</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1082
mockup/control-page-v3.html

@@ -1,1082 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - With Bambu Assets</title>
-  <style>
-    * {
-      margin: 0;
-      padding: 0;
-      box-sizing: border-box;
-    }
-
-    :root {
-      --bg-primary: #1a1a1a;
-      --bg-secondary: #2d2d2d;
-      --bg-tertiary: #3d3d3d;
-      --bg-card: #252525;
-      --text-primary: #ffffff;
-      --text-secondary: #888888;
-      --text-muted: #666666;
-      --accent: #00ae42;
-      --accent-hover: #00c94b;
-      --orange: #f5a623;
-      --red: #e74c3c;
-      --border: #404040;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-primary);
-      color: var(--text-primary);
-      min-height: 100vh;
-      font-size: 13px;
-    }
-
-    /* Icon styling */
-    .icon {
-      width: 18px;
-      height: 18px;
-      fill: currentColor;
-    }
-
-    .icon-sm {
-      width: 14px;
-      height: 14px;
-    }
-
-    .icon-lg {
-      width: 24px;
-      height: 24px;
-    }
-
-    /* Printer Tabs */
-    .printer-tabs {
-      display: flex;
-      background: var(--bg-secondary);
-      border-bottom: 1px solid var(--border);
-      padding: 0 12px;
-    }
-
-    .printer-tab {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 10px 16px;
-      border: none;
-      background: none;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 13px;
-      border-bottom: 2px solid transparent;
-      margin-bottom: -1px;
-    }
-
-    .printer-tab:hover { color: var(--text-primary); }
-    .printer-tab.active {
-      color: var(--text-primary);
-      border-bottom-color: var(--accent);
-    }
-
-    .status-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      background: var(--accent);
-    }
-    .status-dot.offline { background: var(--red); }
-    .status-dot.printing { background: var(--orange); }
-
-    .status-badge {
-      font-size: 10px;
-      padding: 2px 6px;
-      background: var(--bg-tertiary);
-      border-radius: 3px;
-      color: var(--text-secondary);
-    }
-
-    /* Main Layout */
-    .main-content {
-      display: grid;
-      grid-template-columns: 1fr 360px;
-      height: calc(100vh - 41px);
-    }
-
-    /* Left Column */
-    .left-column {
-      display: flex;
-      flex-direction: column;
-      background: #000;
-    }
-
-    .camera-section {
-      flex: 1;
-      position: relative;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 100%);
-    }
-
-    .camera-placeholder {
-      color: var(--text-muted);
-      font-size: 14px;
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .camera-controls {
-      position: absolute;
-      top: 8px;
-      left: 8px;
-      display: flex;
-      gap: 4px;
-    }
-
-    .cam-btn {
-      padding: 4px 10px;
-      background: rgba(0,0,0,0.7);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      color: var(--text-primary);
-      font-size: 11px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .cam-btn:hover { background: rgba(0,0,0,0.9); }
-    .cam-btn .icon { width: 14px; height: 14px; }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-secondary);
-      padding: 12px 16px;
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      border-top: 1px solid var(--border);
-    }
-
-    .print-thumb {
-      width: 56px;
-      height: 56px;
-      background: var(--bg-tertiary);
-      border-radius: 6px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .print-info { flex: 1; }
-    .print-name { font-weight: 500; margin-bottom: 6px; }
-
-    .progress-bar {
-      height: 4px;
-      background: var(--bg-tertiary);
-      border-radius: 2px;
-      margin-bottom: 6px;
-    }
-
-    .progress-fill {
-      height: 100%;
-      background: var(--accent);
-      border-radius: 2px;
-      width: 0%;
-    }
-
-    .print-stats {
-      display: flex;
-      gap: 20px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-    .print-stats span { color: var(--text-primary); }
-
-    .print-btns { display: flex; gap: 6px; }
-
-    .print-btn {
-      width: 36px;
-      height: 36px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-    }
-    .print-btn.pause { background: var(--orange); color: white; }
-    .print-btn.stop { background: var(--red); color: white; }
-    .print-btn:disabled { opacity: 0.4; cursor: not-allowed; }
-
-    /* Right Panel */
-    .control-panel {
-      background: var(--bg-secondary);
-      overflow-y: auto;
-      padding: 12px;
-      display: flex;
-      flex-direction: column;
-      gap: 16px;
-    }
-
-    .section-label {
-      font-size: 11px;
-      color: var(--text-secondary);
-      text-transform: uppercase;
-      letter-spacing: 0.5px;
-      margin-bottom: 8px;
-    }
-
-    /* Temperature Grid */
-    .temp-grid {
-      display: grid;
-      grid-template-columns: 1fr 1fr;
-      gap: 6px;
-    }
-
-    .temp-item {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 10px 12px;
-      background: var(--bg-card);
-      border-radius: 8px;
-      cursor: pointer;
-      transition: background 0.15s;
-    }
-
-    .temp-item:hover { background: var(--bg-tertiary); }
-
-    .temp-icon {
-      width: 20px;
-      height: 20px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .temp-icon img {
-      width: 100%;
-      height: 100%;
-      filter: brightness(0) invert(1);
-      opacity: 0.7;
-    }
-
-    .temp-label {
-      font-size: 11px;
-      color: var(--accent);
-      font-weight: 600;
-      min-width: 14px;
-    }
-
-    .temp-value {
-      font-size: 18px;
-      font-weight: 500;
-    }
-
-    .temp-target {
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    /* Quick Buttons */
-    .quick-row {
-      display: flex;
-      gap: 6px;
-      margin-top: 8px;
-    }
-
-    .quick-btn {
-      flex: 1;
-      padding: 10px 8px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 8px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 6px;
-      transition: all 0.15s;
-    }
-
-    .quick-btn:hover {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    .quick-btn.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    .quick-btn img {
-      width: 16px;
-      height: 16px;
-      filter: brightness(0) invert(0.6);
-    }
-
-    .quick-btn:hover img,
-    .quick-btn.active img {
-      filter: brightness(0) invert(1);
-    }
-
-    /* Speed */
-    .speed-row {
-      display: flex;
-      gap: 4px;
-    }
-
-    .speed-btn {
-      flex: 1;
-      padding: 10px 4px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 8px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-      transition: all 0.15s;
-    }
-
-    .speed-btn:hover { background: var(--bg-tertiary); }
-    .speed-btn.active { background: var(--accent); color: white; }
-    .speed-btn.ludicrous { color: var(--red); }
-    .speed-btn.ludicrous.active { background: var(--red); color: white; }
-
-    /* Movement */
-    .movement-row {
-      display: flex;
-      gap: 16px;
-      align-items: flex-start;
-    }
-
-    /* Jog Pad */
-    .jog-container {
-      position: relative;
-      width: 100px;
-      height: 100px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: var(--bg-card);
-      position: relative;
-    }
-
-    .jog-btn {
-      position: absolute;
-      width: 28px;
-      height: 28px;
-      background: var(--bg-tertiary);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 12px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--accent); color: white; }
-    .jog-btn.up { top: 4px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 4px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 4px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 4px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 36px;
-      height: 36px;
-      background: var(--accent);
-      border: none;
-      border-radius: 50%;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: var(--accent-hover); }
-    .jog-home img {
-      width: 18px;
-      height: 18px;
-      filter: brightness(0) invert(1);
-    }
-
-    /* Z Control */
-    .z-col {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .z-btn {
-      width: 32px;
-      height: 28px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-    }
-
-    .z-btn:hover { background: var(--bg-tertiary); color: var(--text-primary); }
-    .z-label { font-size: 10px; color: var(--text-muted); }
-
-    /* Steps */
-    .step-col {
-      display: flex;
-      flex-direction: column;
-      gap: 3px;
-    }
-
-    .step-btn {
-      padding: 6px 10px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 6px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 10px;
-    }
-
-    .step-btn:hover, .step-btn.active {
-      background: var(--bg-tertiary);
-      color: var(--text-primary);
-    }
-
-    /* Extruder */
-    .extruder-col {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .extruder-toggle {
-      display: flex;
-      background: var(--bg-card);
-      border-radius: 6px;
-      overflow: hidden;
-    }
-
-    .extruder-toggle button {
-      padding: 6px 14px;
-      border: none;
-      background: none;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 11px;
-    }
-
-    .extruder-toggle button.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    .extruder-graphic {
-      height: 90px;
-    }
-
-    .extruder-graphic img {
-      height: 100%;
-      width: auto;
-    }
-
-    .extruder-btns {
-      display: flex;
-      flex-direction: column;
-      gap: 2px;
-    }
-
-    .ext-btn {
-      width: 28px;
-      height: 22px;
-      background: var(--bg-card);
-      border: none;
-      border-radius: 4px;
-      color: var(--text-secondary);
-      cursor: pointer;
-      font-size: 10px;
-    }
-
-    .ext-btn:hover { background: var(--bg-tertiary); }
-
-    /* AMS Section */
-    .ams-section {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-    }
-
-    /* Nozzle Assignment */
-    .nozzle-row {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 10px;
-    }
-
-    .nozzle-group {
-      flex: 1;
-      display: flex;
-      align-items: center;
-      padding: 6px 10px;
-      background: var(--bg-card);
-      border-radius: 6px;
-      border: 2px solid transparent;
-      cursor: pointer;
-    }
-
-    .nozzle-group:hover { background: var(--bg-tertiary); }
-    .nozzle-group.active { border-color: var(--accent); }
-
-    .nozzle-colors {
-      display: flex;
-      gap: 3px;
-    }
-
-    .nozzle-dot {
-      width: 14px;
-      height: 14px;
-      border-radius: 3px;
-      border: 1px solid rgba(255,255,255,0.2);
-    }
-
-    .nozzle-dot.empty {
-      background: var(--bg-tertiary) !important;
-      border-style: dashed;
-    }
-
-    .nozzle-sep {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      color: var(--text-muted);
-      font-size: 11px;
-    }
-
-    /* AMS Tabs */
-    .ams-tabs {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      gap: 3px;
-      padding: 5px 8px;
-      background: var(--bg-card);
-      border: 2px solid transparent;
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { background: var(--bg-tertiary); }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(255,255,255,0.15);
-    }
-
-    .ams-tab-num {
-      font-size: 10px;
-      color: var(--text-secondary);
-      margin-left: 2px;
-    }
-
-    /* AMS Content */
-    .ams-content {
-      background: var(--bg-card);
-      border-radius: 8px;
-      padding: 10px;
-    }
-
-    .ams-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 10px;
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    .ams-header-info {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .ams-header-info img {
-      width: 12px;
-      height: 12px;
-      filter: brightness(0) invert(0.5);
-    }
-
-    .ams-slots {
-      display: flex;
-      gap: 8px;
-      justify-content: center;
-    }
-
-    .ams-slot {
-      width: 56px;
-      cursor: pointer;
-      border: 2px solid transparent;
-      border-radius: 8px;
-      overflow: hidden;
-      transition: all 0.15s;
-      background: var(--bg-secondary);
-    }
-
-    .ams-slot:hover { border-color: var(--text-muted); }
-    .ams-slot.active { border-color: var(--accent); }
-    .ams-slot.empty { opacity: 0.4; }
-
-    .ams-slot-color {
-      height: 42px;
-      display: flex;
-      align-items: flex-end;
-      justify-content: center;
-      padding-bottom: 4px;
-    }
-
-    .ams-slot-color img {
-      width: 16px;
-      height: 16px;
-      opacity: 0.6;
-    }
-
-    .ams-slot-info {
-      background: var(--bg-tertiary);
-      padding: 4px;
-      text-align: center;
-    }
-
-    .ams-slot-type {
-      font-size: 10px;
-      font-weight: 600;
-      color: var(--text-primary);
-    }
-
-    .ams-slot-num {
-      font-size: 8px;
-      color: var(--text-muted);
-    }
-
-    /* External */
-    .ext-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-top: 10px;
-    }
-
-    .ext-label {
-      font-size: 11px;
-      color: var(--text-secondary);
-      width: 24px;
-    }
-
-    .ext-slot {
-      width: 56px;
-      height: 62px;
-      background: var(--bg-secondary);
-      border-radius: 8px;
-      border: 2px solid transparent;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      font-size: 16px;
-      color: var(--text-muted);
-    }
-
-    .ext-slot:hover { border-color: var(--text-muted); }
-    .ext-slot-label { font-size: 8px; margin-top: 2px; }
-
-    .spool-holder {
-      flex: 1;
-      height: 62px;
-      background: var(--bg-secondary);
-      border-radius: 8px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .spool-holder img {
-      height: 50px;
-      opacity: 0.7;
-    }
-
-    /* AMS Buttons */
-    .ams-btns {
-      display: flex;
-      gap: 6px;
-      margin-top: 10px;
-    }
-
-    .ams-btn {
-      flex: 1;
-      padding: 10px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      font-weight: 500;
-    }
-
-    .ams-btn.secondary { background: var(--bg-card); color: var(--text-primary); }
-    .ams-btn.secondary:hover { background: var(--bg-tertiary); }
-    .ams-btn.primary { background: var(--accent); color: white; }
-    .ams-btn.primary:hover { background: var(--accent-hover); }
-
-    /* AMS Animation */
-    .ams-animation {
-      flex: 1;
-      min-height: 100px;
-      margin-top: 12px;
-      background: var(--bg-card);
-      border-radius: 8px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      gap: 12px;
-      padding: 16px;
-    }
-
-    .ams-graphic {
-      display: flex;
-      align-items: flex-end;
-      gap: 8px;
-    }
-
-    .ams-graphic img {
-      height: 40px;
-      opacity: 0.8;
-    }
-
-    .hub-label {
-      font-size: 10px;
-      color: var(--text-muted);
-      padding: 4px 12px;
-      background: var(--bg-tertiary);
-      border-radius: 4px;
-    }
-
-    .ams-status {
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-  </style>
-</head>
-<body>
-  <!-- Printer Tabs -->
-  <div class="printer-tabs">
-    <button class="printer-tab active">
-      <span class="status-dot"></span>
-      H2D-1
-      <span class="status-badge">IDLE</span>
-    </button>
-    <button class="printer-tab">
-      <span class="status-dot printing"></span>
-      X1C-2
-    </button>
-    <button class="printer-tab">
-      <span class="status-dot offline"></span>
-      P1S-3
-    </button>
-  </div>
-
-  <div class="main-content">
-    <!-- Left: Camera + Progress -->
-    <div class="left-column">
-      <div class="camera-section">
-        <div class="camera-controls">
-          <button class="cam-btn">
-            <img src="icons/video-camera.svg" class="icon" alt="">
-            Start
-          </button>
-          <button class="cam-btn">⛶ Fullscreen</button>
-        </div>
-        <span class="camera-placeholder">
-          <img src="icons/webcam.svg" style="width: 24px; height: 24px; filter: brightness(0) invert(0.4);">
-          Camera Feed
-        </span>
-      </div>
-
-      <div class="print-progress">
-        <div class="print-thumb">No Print</div>
-        <div class="print-info">
-          <div class="print-name">Idle</div>
-          <div class="progress-bar"><div class="progress-fill"></div></div>
-          <div class="print-stats">
-            Layer: <span>--/--</span>
-            Time: <span>--:--</span>
-            Remaining: <span>--:--</span>
-          </div>
-        </div>
-        <div class="print-btns">
-          <button class="print-btn pause" disabled>⏸</button>
-          <button class="print-btn stop" disabled>⏹</button>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right: Control Panel -->
-    <div class="control-panel">
-      <!-- Temperature -->
-      <div>
-        <div class="section-label">Temperature</div>
-        <div class="temp-grid">
-          <div class="temp-item">
-            <div class="temp-icon"><img src="icons/hotend.svg" alt=""></div>
-            <span class="temp-label">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-          <div class="temp-item">
-            <div class="temp-icon"><img src="icons/hotend.svg" alt=""></div>
-            <span class="temp-label">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-          <div class="temp-item">
-            <div class="temp-icon"><img src="icons/heatbed.svg" alt=""></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-          <div class="temp-item">
-            <div class="temp-icon"><img src="icons/chamber.svg" alt=""></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0°C</span>
-          </div>
-        </div>
-        <div class="quick-row">
-          <button class="quick-btn">
-            <img src="icons/ventilation.svg" alt="">
-            Fans
-          </button>
-          <button class="quick-btn active">
-            <img src="icons/lamp.svg" alt="">
-            Lamp
-          </button>
-          <button class="quick-btn">
-            <img src="icons/snowflake.svg" alt="">
-            Cool
-          </button>
-        </div>
-      </div>
-
-      <!-- Speed -->
-      <div>
-        <div class="section-label">Print Speed</div>
-        <div class="speed-row">
-          <button class="speed-btn">Silent</button>
-          <button class="speed-btn active">Standard</button>
-          <button class="speed-btn">Sport</button>
-          <button class="speed-btn ludicrous">Ludicrous</button>
-        </div>
-      </div>
-
-      <!-- Movement -->
-      <div>
-        <div class="section-label">Movement</div>
-        <div class="movement-row">
-          <!-- XY Jog -->
-          <div class="jog-container">
-            <div class="jog-ring">
-              <button class="jog-btn up">▲</button>
-              <button class="jog-btn down">▼</button>
-              <button class="jog-btn left">◀</button>
-              <button class="jog-btn right">▶</button>
-              <button class="jog-home">
-                <img src="icons/home.svg" alt="">
-              </button>
-            </div>
-          </div>
-
-          <!-- Z -->
-          <div class="z-col">
-            <button class="z-btn">▲</button>
-            <span class="z-label">Z</span>
-            <button class="z-btn">▼</button>
-          </div>
-
-          <!-- Steps -->
-          <div class="step-col">
-            <button class="step-btn">10mm</button>
-            <button class="step-btn active">1mm</button>
-            <button class="step-btn">0.1mm</button>
-          </div>
-
-          <!-- Extruder -->
-          <div class="extruder-col">
-            <div class="extruder-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic">
-              <img src="icons/dual-extruder.png" alt="Extruder">
-            </div>
-            <div class="extruder-btns">
-              <button class="ext-btn">▲</button>
-              <button class="ext-btn">▼</button>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <!-- AMS -->
-      <div class="ams-section">
-        <div class="section-label">AMS</div>
-
-        <!-- Nozzle Assignment -->
-        <div class="nozzle-row">
-          <div class="nozzle-group active">
-            <div class="nozzle-colors">
-              <div class="nozzle-dot" style="background: #FFD700;"></div>
-              <div class="nozzle-dot" style="background: #8B4513;"></div>
-              <div class="nozzle-dot" style="background: #1a1a1a;"></div>
-              <div class="nozzle-dot" style="background: #222;"></div>
-            </div>
-          </div>
-          <div class="nozzle-sep">
-            <div class="nozzle-dot" style="background: #0066FF;"></div>
-            <span>/</span>
-            <div class="nozzle-dot" style="background: #FF0000;"></div>
-          </div>
-          <div class="nozzle-group">
-            <div class="nozzle-colors">
-              <div class="nozzle-dot empty"></div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Tabs -->
-        <div class="ams-tabs">
-          <div class="ams-tab active">
-            <div class="ams-tab-dot" style="background: #FFD700;"></div>
-            <div class="ams-tab-dot" style="background: #1a1a1a;"></div>
-            <div class="ams-tab-dot" style="background: #8B4513;"></div>
-            <div class="ams-tab-dot" style="background: #222;"></div>
-            <span class="ams-tab-num">1</span>
-          </div>
-          <div class="ams-tab">
-            <div class="ams-tab-dot" style="background: #FF0000;"></div>
-            <div class="ams-tab-dot" style="background: #0066FF;"></div>
-            <div class="ams-tab-dot" style="background: #3d3d3d;"></div>
-            <div class="ams-tab-dot" style="background: #3d3d3d;"></div>
-            <span class="ams-tab-num">2</span>
-          </div>
-          <div class="ams-tab">
-            <div class="ams-tab-dot" style="background: #00FF00;"></div>
-            <div class="ams-tab-dot" style="background: #FFF;"></div>
-            <div class="ams-tab-dot" style="background: #FF69B4;"></div>
-            <div class="ams-tab-dot" style="background: #9932CC;"></div>
-            <span class="ams-tab-num">3</span>
-          </div>
-        </div>
-
-        <!-- AMS Slots -->
-        <div class="ams-content">
-          <div class="ams-header">
-            <span>AMS 1</span>
-            <div class="ams-header-info">
-              <img src="icons/water.svg" alt=""> 18%
-              <span style="margin: 0 4px;">·</span>
-              🌡 24°C
-            </div>
-          </div>
-          <div class="ams-slots">
-            <div class="ams-slot active">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #FFD700 0%, #E6C200 100%);">
-                <img src="icons/eye.svg" alt="">
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PLA</div>
-                <div class="ams-slot-num">A1</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%);">
-                <img src="icons/eye.svg" alt="" style="filter: invert(1);">
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PETG</div>
-                <div class="ams-slot-num">A2</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #8B4513 0%, #6B3510 100%);">
-                <img src="icons/eye.svg" alt="" style="filter: invert(1);">
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PETG</div>
-                <div class="ams-slot-num">A3</div>
-              </div>
-            </div>
-            <div class="ams-slot">
-              <div class="ams-slot-color" style="background: linear-gradient(180deg, #222 0%, #111 100%);">
-                <img src="icons/eye.svg" alt="" style="filter: invert(1);">
-              </div>
-              <div class="ams-slot-info">
-                <div class="ams-slot-type">PLA</div>
-                <div class="ams-slot-num">A4</div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- External -->
-        <div class="ext-row">
-          <span class="ext-label">Ext</span>
-          <div class="ext-slot">
-            ?
-            <span class="ext-slot-label">Spool</span>
-          </div>
-          <div class="spool-holder">
-            <img src="icons/single-extruder1.png" alt="Spool holder">
-          </div>
-        </div>
-
-        <!-- Buttons -->
-        <div class="ams-btns">
-          <button class="ams-btn secondary">Unload</button>
-          <button class="ams-btn primary">Load</button>
-        </div>
-
-        <!-- Animation Area -->
-        <div class="ams-animation">
-          <div class="ams-graphic">
-            <img src="icons/ams.png" alt="AMS">
-          </div>
-          <div class="hub-label">HUB</div>
-          <div class="ams-status">Ready - Select a slot to load</div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1051
mockup/control-page-v4.html

@@ -1,1051 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - Bambu Studio Exact</title>
-  <style>
-    * {
-      margin: 0;
-      padding: 0;
-      box-sizing: border-box;
-    }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-hover: #f0f0f0;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-      --accent-light: #e8f5e9;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    /* Main Layout */
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 320px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 4px;
-    }
-
-    .camera-title {
-      font-size: 12px;
-      color: var(--text-secondary);
-      margin-right: auto;
-    }
-
-    .camera-icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .camera-icon-btn:hover {
-      background: var(--bg-hover);
-    }
-
-    .camera-icon-btn img {
-      width: 18px;
-      height: 18px;
-      opacity: 0.6;
-    }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #666;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      border-top: 1px solid var(--border);
-      padding: 12px 16px;
-    }
-
-    .progress-header {
-      font-size: 11px;
-      color: var(--text-muted);
-      margin-bottom: 10px;
-    }
-
-    .progress-content {
-      display: flex;
-      gap: 12px;
-    }
-
-    .progress-thumb {
-      width: 60px;
-      height: 60px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .progress-thumb img {
-      width: 40px;
-      opacity: 0.5;
-    }
-
-    .progress-info {
-      flex: 1;
-    }
-
-    .progress-name {
-      font-weight: 500;
-      margin-bottom: 2px;
-    }
-
-    .progress-status {
-      color: var(--accent);
-      font-size: 12px;
-      margin-bottom: 6px;
-    }
-
-    .progress-bar-container {
-      height: 4px;
-      background: var(--border-light);
-      border-radius: 2px;
-      margin-bottom: 6px;
-    }
-
-    .progress-bar-fill {
-      height: 100%;
-      background: var(--accent);
-      border-radius: 2px;
-      width: 0%;
-    }
-
-    .progress-details {
-      display: flex;
-      gap: 16px;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .progress-actions {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-      align-items: flex-end;
-    }
-
-    .progress-icons {
-      display: flex;
-      gap: 4px;
-    }
-
-    .progress-icon-btn {
-      width: 24px;
-      height: 24px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .progress-icon-btn:hover {
-      background: var(--bg-hover);
-    }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .header-btn {
-      padding: 4px 10px;
-      font-size: 11px;
-      border: none;
-      border-radius: 4px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-
-    .header-btn:hover {
-      opacity: 0.9;
-    }
-
-    .control-content {
-      padding: 12px;
-      display: flex;
-      flex-direction: column;
-      gap: 16px;
-    }
-
-    /* Temperature Section */
-    .temp-list {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 6px 0;
-    }
-
-    .temp-icon {
-      width: 20px;
-      height: 20px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .temp-icon img {
-      width: 18px;
-      height: 18px;
-      opacity: 0.7;
-    }
-
-    .temp-badge {
-      font-size: 10px;
-      font-weight: 600;
-      color: var(--accent);
-      background: var(--accent-light);
-      padding: 1px 4px;
-      border-radius: 3px;
-    }
-
-    .temp-value {
-      font-size: 15px;
-      font-weight: 500;
-    }
-
-    .temp-target {
-      font-size: 13px;
-      color: var(--text-muted);
-    }
-
-    .temp-unit {
-      font-size: 13px;
-      color: var(--text-muted);
-    }
-
-    /* Air Condition */
-    .air-row {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 8px 0;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img {
-      width: 16px;
-      height: 16px;
-      opacity: 0.6;
-    }
-
-    .air-item {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 6px 12px;
-      background: var(--bg-light);
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .air-item:hover {
-      background: var(--bg-hover);
-    }
-
-    .air-item img {
-      width: 16px;
-      height: 16px;
-      opacity: 0.6;
-    }
-
-    .lamp-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 50%;
-      background: var(--accent);
-    }
-
-    /* Movement Section */
-    .movement-section {
-      display: flex;
-      gap: 12px;
-      padding-top: 8px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .jog-area {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 8px;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-    }
-
-    .nozzle-toggle button {
-      padding: 4px 16px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 11px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    .jog-pad {
-      position: relative;
-      width: 120px;
-      height: 120px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.y-plus { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.y-minus { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.x-minus { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.x-plus { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-secondary);
-    }
-
-    .jog-btn:hover {
-      background: var(--accent);
-      color: white;
-      border-color: var(--accent);
-    }
-
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-    }
-
-    .jog-home img {
-      width: 18px;
-      height: 18px;
-      filter: brightness(0) invert(1);
-    }
-
-    .step-col {
-      display: flex;
-      flex-direction: column;
-      gap: 2px;
-      font-size: 10px;
-    }
-
-    .step-btn {
-      padding: 4px 8px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      color: var(--text-secondary);
-      font-size: 10px;
-    }
-
-    .step-btn:hover, .step-btn.active {
-      background: var(--bg-hover);
-      color: var(--text-primary);
-    }
-
-    .step-label {
-      text-align: center;
-      color: var(--text-muted);
-      padding: 2px 0;
-    }
-
-    /* Extruder */
-    .extruder-area {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .extruder-label {
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .extruder-graphic {
-      height: 80px;
-    }
-
-    .extruder-graphic img {
-      height: 100%;
-    }
-
-    .extruder-btns {
-      display: flex;
-      flex-direction: column;
-      gap: 2px;
-    }
-
-    .ext-btn {
-      width: 24px;
-      height: 20px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 9px;
-      color: var(--text-secondary);
-    }
-
-    .ext-btn:hover {
-      background: var(--bg-hover);
-    }
-
-    /* AMS Nozzle Colors */
-    .nozzle-colors-row {
-      display: flex;
-      gap: 8px;
-      padding: 8px 0;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .nozzle-color-box {
-      flex: 1;
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      padding: 6px 10px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-    }
-
-    .nozzle-color-box.active {
-      border-color: var(--accent);
-    }
-
-    .color-dot {
-      width: 14px;
-      height: 14px;
-      border-radius: 3px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .color-dot.empty {
-      background: var(--bg-light) !important;
-      border: 1px dashed var(--border);
-    }
-
-    /* AMS Section */
-    .ams-section {
-      border-top: 1px solid var(--border-light);
-      padding-top: 12px;
-    }
-
-    .ams-header-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-bottom: 8px;
-    }
-
-    .ams-humidity {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .ams-humidity img {
-      width: 12px;
-      height: 12px;
-      opacity: 0.5;
-    }
-
-    .ams-slots-row {
-      display: flex;
-      gap: 4px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot-label {
-      font-size: 9px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      gap: 2px;
-    }
-
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-    }
-
-    .ams-slot {
-      width: 52px;
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover {
-      border-color: var(--text-muted);
-    }
-
-    .ams-slot.active {
-      border-color: var(--accent);
-    }
-
-    .ams-slot-color {
-      height: 36px;
-      display: flex;
-      align-items: flex-end;
-      justify-content: center;
-      padding-bottom: 4px;
-    }
-
-    .ams-slot-color img {
-      width: 14px;
-      height: 14px;
-      opacity: 0.5;
-    }
-
-    .ams-slot-info {
-      background: var(--bg-light);
-      padding: 4px;
-      text-align: center;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-slot-type {
-      font-size: 10px;
-      font-weight: 600;
-      color: var(--text-primary);
-    }
-
-    /* AMS Connector Lines */
-    .ams-connector {
-      display: flex;
-      justify-content: center;
-      padding: 8px 0;
-      position: relative;
-    }
-
-    .connector-lines {
-      display: flex;
-      gap: 20px;
-    }
-
-    .connector-line {
-      width: 2px;
-      height: 20px;
-      background: var(--border);
-    }
-
-    .connector-hub {
-      position: absolute;
-      bottom: 0;
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    /* External Spool */
-    .ext-spool-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 8px 0;
-    }
-
-    .ext-label {
-      font-size: 11px;
-      color: var(--text-muted);
-      width: 24px;
-    }
-
-    .ext-slot {
-      width: 52px;
-      height: 52px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      font-size: 16px;
-      color: var(--text-muted);
-      cursor: pointer;
-    }
-
-    .ext-slot:hover {
-      background: var(--bg-hover);
-    }
-
-    .ext-slot-label {
-      font-size: 8px;
-    }
-
-    .spool-graphic {
-      flex: 1;
-      height: 52px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .spool-graphic img {
-      height: 45px;
-      opacity: 0.8;
-    }
-
-    /* AMS Buttons */
-    .ams-actions {
-      display: flex;
-      gap: 8px;
-      padding-top: 8px;
-    }
-
-    .ams-action-btn {
-      padding: 6px 12px;
-      font-size: 11px;
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .ams-action-btn.auto {
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      color: var(--text-secondary);
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .ams-action-btn.auto img {
-      width: 14px;
-      height: 14px;
-      opacity: 0.5;
-    }
-
-    .ams-action-btn.auto:hover {
-      background: var(--bg-hover);
-    }
-
-    .ams-spacer {
-      flex: 1;
-    }
-
-    .ams-action-btn.unload {
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn.unload:hover {
-      background: var(--bg-hover);
-    }
-
-    .ams-action-btn.load {
-      background: var(--accent);
-      border: none;
-      color: white;
-    }
-
-    .ams-action-btn.load:hover {
-      opacity: 0.9;
-    }
-
-    /* Hub Graphic */
-    .hub-area {
-      display: flex;
-      justify-content: center;
-      padding: 12px 0;
-    }
-
-    .hub-graphic {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .hub-line {
-      width: 30px;
-      height: 2px;
-      background: var(--border);
-    }
-
-    .hub-box {
-      padding: 4px 12px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="camera-icon-btn"><img src="icons/video-camera.svg" alt=""></button>
-        <button class="camera-icon-btn"><img src="icons/webcam.svg" alt=""></button>
-        <button class="camera-icon-btn"><img src="icons/settings.svg" alt=""></button>
-        <button class="camera-icon-btn"><img src="icons/reload.svg" alt=""></button>
-      </div>
-
-      <div class="camera-feed">
-        Camera Feed
-      </div>
-
-      <div class="print-progress">
-        <div class="progress-header">Printing Progress</div>
-        <div class="progress-content">
-          <div class="progress-thumb">
-            <img src="icons/micro-sd.svg" alt="">
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-container">
-              <div class="progress-bar-fill"></div>
-            </div>
-            <div class="progress-details">
-              <span>Layer: N/A &nbsp; N/A</span>
-            </div>
-            <div class="progress-details" style="margin-top: 4px;">
-              <span>Estimated finish time: N/A</span>
-            </div>
-          </div>
-          <div class="progress-actions">
-            <div class="progress-icons">
-              <button class="progress-icon-btn">⌂</button>
-              <button class="progress-icon-btn">⏸</button>
-              <button class="progress-icon-btn">⏹</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-content">
-        <!-- Temperature -->
-        <div class="temp-list">
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg" alt=""></div>
-            <span class="temp-badge">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0</span>
-            <span class="temp-unit">°C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg" alt=""></div>
-            <span class="temp-badge">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0</span>
-            <span class="temp-unit">°C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/heatbed.svg" alt=""></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0</span>
-            <span class="temp-unit">°C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/chamber.svg" alt=""></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0</span>
-            <span class="temp-unit">°C</span>
-          </div>
-        </div>
-
-        <!-- Air Condition -->
-        <div class="air-row">
-          <div class="air-label">
-            <img src="icons/ventilation.svg" alt="">
-            Air Condition
-          </div>
-          <div class="air-item">
-            <img src="icons/ventilation.svg" alt="">
-            <span>100%</span>
-          </div>
-          <div class="air-item">
-            <span>Lamp</span>
-            <div class="lamp-dot"></div>
-          </div>
-        </div>
-
-        <!-- Movement -->
-        <div class="movement-section">
-          <div class="jog-area">
-            <div class="jog-pad">
-              <div class="jog-ring">
-                <span class="jog-label y-plus">Y</span>
-                <span class="jog-label y-minus">-Y</span>
-                <span class="jog-label x-minus">-X</span>
-                <span class="jog-label x-plus">X</span>
-                <button class="jog-btn up">▲</button>
-                <button class="jog-btn down">▼</button>
-                <button class="jog-btn left">◀</button>
-                <button class="jog-btn right">▶</button>
-                <button class="jog-home">
-                  <img src="icons/home.svg" alt="">
-                </button>
-              </div>
-            </div>
-          </div>
-
-          <div class="step-col">
-            <button class="step-btn">↑ 10</button>
-            <button class="step-btn active">↑ 1</button>
-            <span class="step-label">Bed</span>
-            <button class="step-btn">↓ 1</button>
-            <button class="step-btn">↓ 10</button>
-          </div>
-
-          <div class="extruder-area">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic">
-              <img src="icons/dual-extruder.png" alt="">
-            </div>
-            <span class="extruder-label">Extruder</span>
-            <div class="extruder-btns">
-              <button class="ext-btn">▲</button>
-              <button class="ext-btn">▼</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- Nozzle Color Assignment -->
-        <div class="nozzle-colors-row">
-          <div class="nozzle-color-box active">
-            <div class="color-dot" style="background: #FFD700;"></div>
-            <div class="color-dot" style="background: #8B4513;"></div>
-            <div class="color-dot" style="background: #1a1a1a;"></div>
-            <div class="color-dot" style="background: #222;"></div>
-          </div>
-          <div class="nozzle-color-box">
-            <div class="color-dot empty"></div>
-          </div>
-        </div>
-
-        <!-- AMS -->
-        <div class="ams-section">
-          <div class="ams-header-row">
-            <div class="ams-humidity">
-              <img src="icons/water.svg" alt="">
-              18 %
-            </div>
-            <div class="ams-humidity">
-              ☀
-            </div>
-          </div>
-
-          <div class="ams-slots-row">
-            <span class="ams-slot-label">A1 <img src="icons/reload.svg" style="width:10px;opacity:0.4"></span>
-            <span class="ams-slot-label">A2 <img src="icons/reload.svg" style="width:10px;opacity:0.4"></span>
-            <span class="ams-slot-label">A3 <img src="icons/reload.svg" style="width:10px;opacity:0.4"></span>
-            <span class="ams-slot-label">A4 <img src="icons/reload.svg" style="width:10px;opacity:0.4"></span>
-            <span style="flex:1"></span>
-            <span class="ams-slot-label">Ext</span>
-          </div>
-
-          <div style="display: flex; gap: 12px;">
-            <div class="ams-slots">
-              <div class="ams-slot active">
-                <div class="ams-slot-color" style="background: linear-gradient(180deg, #FFD700 60%, #E6C200 100%);">
-                  <img src="icons/eye.svg" alt="">
-                </div>
-                <div class="ams-slot-info">
-                  <div class="ams-slot-type">PLA</div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-color" style="background: linear-gradient(180deg, #1a1a1a 60%, #0a0a0a 100%);">
-                  <img src="icons/eye.svg" alt="" style="filter: invert(1);">
-                </div>
-                <div class="ams-slot-info">
-                  <div class="ams-slot-type">PETG</div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-color" style="background: linear-gradient(180deg, #8B4513 60%, #6B3510 100%);">
-                  <img src="icons/eye.svg" alt="" style="filter: invert(1);">
-                </div>
-                <div class="ams-slot-info">
-                  <div class="ams-slot-type">PETG</div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-color" style="background: linear-gradient(180deg, #222 60%, #111 100%);">
-                  <img src="icons/eye.svg" alt="" style="filter: invert(1);">
-                </div>
-                <div class="ams-slot-info">
-                  <div class="ams-slot-type">PLA</div>
-                </div>
-              </div>
-            </div>
-
-            <div style="display: flex; flex-direction: column; align-items: center; gap: 4px;">
-              <div class="ext-slot">
-                ?
-                <span class="ext-slot-label">∠</span>
-              </div>
-              <div class="spool-graphic">
-                <img src="icons/single-extruder1.png" alt="">
-              </div>
-            </div>
-          </div>
-
-          <!-- Actions -->
-          <div class="ams-actions">
-            <button class="ams-action-btn auto">
-              Auto-refill
-              <img src="icons/ams-settings.svg" alt="">
-            </button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn unload">Unload</button>
-            <button class="ams-action-btn load">Load</button>
-          </div>
-
-          <!-- Hub -->
-          <div class="hub-area">
-            <div class="hub-graphic">
-              <div class="hub-line"></div>
-              <div class="hub-box">HUB</div>
-              <div class="hub-line"></div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 952
mockup/control-page-v5.html

@@ -1,952 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - Exact Match</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-hover: #f0f0f0;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 340px;
-      height: 100vh;
-    }
-
-    /* Left Panel */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 6px;
-    }
-
-    .camera-title {
-      font-size: 12px;
-      color: var(--text-secondary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 26px;
-      height: 26px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-muted);
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 16px; height: 16px; opacity: 0.5; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      border-top: 1px solid var(--border);
-      padding: 12px 16px;
-    }
-
-    .progress-label {
-      font-size: 11px;
-      color: var(--text-muted);
-      margin-bottom: 8px;
-    }
-
-    .progress-row {
-      display: flex;
-      gap: 12px;
-      align-items: center;
-    }
-
-    .progress-thumb {
-      width: 50px;
-      height: 50px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .progress-thumb img { width: 30px; opacity: 0.4; }
-
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 13px; }
-    .progress-status { color: var(--accent); font-size: 12px; margin: 2px 0 6px; }
-
-    .progress-bar-bg {
-      height: 3px;
-      background: var(--border-light);
-      border-radius: 2px;
-      margin-bottom: 6px;
-    }
-
-    .progress-bar-fill {
-      height: 100%;
-      background: var(--accent);
-      border-radius: 2px;
-      width: 0%;
-    }
-
-    .progress-details {
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .progress-btns {
-      display: flex;
-      gap: 4px;
-    }
-
-    .progress-btn {
-      width: 28px;
-      height: 28px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-muted);
-    }
-
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      border-bottom: 1px solid var(--border);
-      gap: 6px;
-    }
-
-    .control-title {
-      font-size: 13px;
-      margin-right: auto;
-    }
-
-    .header-btn {
-      padding: 5px 12px;
-      font-size: 11px;
-      border: none;
-      border-radius: 4px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-
-    .control-body {
-      padding: 12px;
-    }
-
-    /* Temperature */
-    .temp-section {
-      margin-bottom: 12px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 5px 0;
-    }
-
-    .temp-icon { width: 18px; height: 18px; }
-    .temp-icon img { width: 100%; opacity: 0.6; }
-
-    .temp-badge {
-      font-size: 9px;
-      font-weight: 700;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 1px 5px;
-      border-radius: 3px;
-    }
-
-    .temp-value { font-size: 16px; font-weight: 500; }
-    .temp-target { font-size: 14px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-row {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      padding: 10px 0;
-      border-top: 1px solid var(--border-light);
-      border-bottom: 1px solid var(--border-light);
-      margin-bottom: 12px;
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 14px; opacity: 0.5; }
-
-    .air-item {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 5px 10px;
-      background: var(--bg-light);
-      border-radius: 6px;
-      font-size: 12px;
-    }
-
-    .air-item img { width: 14px; opacity: 0.5; }
-    .lamp-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement */
-    .movement-section {
-      display: flex;
-      gap: 10px;
-      margin-bottom: 16px;
-    }
-
-    .jog-pad {
-      position: relative;
-      width: 100px;
-      height: 100px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 9px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 6px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 6px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 6px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 6px; top: 50%; transform: translateY(-50%); }
-
-    .jog-arrow {
-      position: absolute;
-      width: 20px;
-      height: 20px;
-      background: none;
-      border: none;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .jog-arrow:hover { color: var(--accent); }
-    .jog-arrow.up { top: 18px; left: 50%; transform: translateX(-50%); }
-    .jog-arrow.down { bottom: 18px; left: 50%; transform: translateX(-50%); }
-    .jog-arrow.left { left: 18px; top: 50%; transform: translateY(-50%); }
-    .jog-arrow.right { right: 18px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 32px;
-      height: 32px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home img { width: 16px; filter: brightness(0) invert(1); }
-
-    .step-col {
-      display: flex;
-      flex-direction: column;
-      gap: 2px;
-      font-size: 10px;
-    }
-
-    .step-btn {
-      padding: 4px 8px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 9px;
-      color: var(--text-secondary);
-    }
-
-    .step-btn.active { background: var(--bg-hover); color: var(--text-primary); }
-    .step-label { text-align: center; color: var(--text-muted); font-size: 9px; padding: 2px 0; }
-
-    .extruder-col {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 4px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-    }
-
-    .nozzle-toggle button {
-      padding: 3px 12px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 10px;
-      cursor: pointer;
-    }
-
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .extruder-img { height: 70px; }
-    .extruder-img img { height: 100%; }
-    .extruder-label { font-size: 9px; color: var(--text-muted); }
-
-    .ext-btns { display: flex; flex-direction: column; gap: 2px; }
-    .ext-btn {
-      width: 22px;
-      height: 18px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 3px;
-      cursor: pointer;
-      font-size: 8px;
-      color: var(--text-muted);
-    }
-
-    /* AMS Section */
-    .ams-section {
-      border-top: 1px solid var(--border-light);
-      padding-top: 12px;
-    }
-
-    /* Nozzle Color Boxes */
-    .nozzle-boxes {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 12px;
-    }
-
-    .nozzle-box {
-      flex: 1;
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-    }
-
-    .nozzle-box.active { border-color: var(--accent); }
-
-    .nozzle-dots {
-      display: flex;
-      gap: 3px;
-    }
-
-    .n-dot {
-      width: 16px;
-      height: 16px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .n-dot.empty {
-      background: var(--bg-light) !important;
-      border: 1px dashed var(--border);
-    }
-
-    .nozzle-box-spacer { flex: 1; }
-
-    .nozzle-box-label {
-      font-size: 11px;
-      color: var(--text-muted);
-      padding: 4px 8px;
-      background: var(--bg-light);
-      border-radius: 4px;
-    }
-
-    /* AMS Middle Row - Tabs */
-    .ams-tabs-row {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 10px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      gap: 3px;
-      padding: 4px 8px;
-      background: var(--bg-light);
-      border: 2px solid transparent;
-      border-radius: 6px;
-      cursor: pointer;
-    }
-
-    .ams-tab:hover { background: var(--bg-hover); }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab-dot {
-      width: 12px;
-      height: 12px;
-      border-radius: 3px;
-      border: 1px solid rgba(0,0,0,0.08);
-    }
-
-    /* AMS Content Area */
-    .ams-content {
-      background: var(--bg-light);
-      border-radius: 10px;
-      padding: 12px;
-    }
-
-    .ams-info-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-bottom: 8px;
-      padding-left: 4px;
-    }
-
-    .ams-humidity {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ams-humidity img { width: 14px; opacity: 0.5; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 6px;
-      padding: 0 4px;
-    }
-
-    .slot-label {
-      width: 60px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 2px;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .slot-label img { width: 12px; opacity: 0.4; }
-
-    /* AMS Main Grid */
-    .ams-grid {
-      display: flex;
-      gap: 12px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 8px;
-    }
-
-    .ams-slot {
-      width: 60px;
-      height: 90px;
-      border-radius: 12px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: var(--text-muted); }
-    .ams-slot.active { border-color: var(--accent); }
-
-    .ams-slot-color {
-      flex: 1;
-      display: flex;
-      align-items: flex-end;
-      justify-content: center;
-      padding-bottom: 6px;
-      position: relative;
-    }
-
-    .ams-slot-color img {
-      width: 18px;
-      height: 18px;
-      opacity: 0.7;
-    }
-
-    .ams-slot-bottom {
-      padding: 6px 4px;
-      text-align: center;
-      background: var(--bg-light);
-      border-top: 1px solid var(--border-light);
-    }
-
-    .ams-slot-type {
-      font-size: 11px;
-      font-weight: 600;
-      color: var(--text-primary);
-    }
-
-    /* Connector Lines */
-    .ams-connectors {
-      display: flex;
-      justify-content: center;
-      padding: 8px 0;
-      margin-left: 30px;
-    }
-
-    .connector-group {
-      display: flex;
-      gap: 32px;
-      position: relative;
-    }
-
-    .connector-line {
-      width: 2px;
-      height: 20px;
-      background: #ccc;
-    }
-
-    .connector-bar {
-      position: absolute;
-      bottom: 0;
-      left: 0;
-      right: 0;
-      height: 2px;
-      background: #ccc;
-    }
-
-    /* External Section */
-    .ext-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .ext-title {
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .ext-slot-box {
-      width: 60px;
-      height: 70px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 12px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      font-size: 20px;
-      color: var(--text-muted);
-    }
-
-    .ext-slot-box:hover { border-color: var(--text-muted); }
-    .ext-slot-symbol { font-size: 14px; }
-
-    .spool-holder-img {
-      height: 70px;
-    }
-
-    .spool-holder-img img { height: 100%; }
-
-    /* Hub Row */
-    .hub-row {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      padding: 10px 0;
-      margin-left: 30px;
-    }
-
-    .hub-line { width: 40px; height: 2px; background: #ccc; }
-
-    .hub-graphic {
-      display: flex;
-      align-items: center;
-      gap: 2px;
-      padding: 4px 8px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-    }
-
-    .hub-dot {
-      width: 8px;
-      height: 8px;
-      border-radius: 50%;
-      background: var(--accent);
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-top: 10px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-    .auto-refill-btn img { width: 16px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-btn {
-      padding: 10px 20px;
-      border-radius: 8px;
-      font-size: 12px;
-      cursor: pointer;
-      border: none;
-    }
-
-    .ams-btn.unload {
-      background: #e0e0e0;
-      color: var(--text-secondary);
-    }
-
-    .ams-btn.unload:hover { background: #d5d5d5; }
-
-    .ams-btn.load {
-      background: #e0e0e0;
-      color: var(--text-secondary);
-    }
-
-    .ams-btn.load:hover { background: #d5d5d5; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb"><img src="icons/micro-sd.svg"></div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-details">Layer: N/A &nbsp; N/A</div>
-            <div class="progress-details">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">⌂</button>
-            <button class="progress-btn">⏸</button>
-            <button class="progress-btn">⏹</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Temperature -->
-        <div class="temp-section">
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/chamber.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-        </div>
-
-        <!-- Air Condition -->
-        <div class="air-row">
-          <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-          <div class="air-item"><img src="icons/ventilation.svg"> 100%</div>
-          <div class="air-item">Lamp <div class="lamp-dot"></div></div>
-        </div>
-
-        <!-- Movement -->
-        <div class="movement-section">
-          <div class="jog-pad">
-            <div class="jog-ring">
-              <span class="jog-label top">Y</span>
-              <span class="jog-label bottom">-Y</span>
-              <span class="jog-label left">-X</span>
-              <span class="jog-label right">X</span>
-              <button class="jog-arrow up">▲</button>
-              <button class="jog-arrow down">▼</button>
-              <button class="jog-arrow left">◀</button>
-              <button class="jog-arrow right">▶</button>
-              <button class="jog-home"><img src="icons/home.svg"></button>
-            </div>
-          </div>
-
-          <div class="step-col">
-            <button class="step-btn">↑ 10</button>
-            <button class="step-btn active">↑ 1</button>
-            <span class="step-label">Bed</span>
-            <button class="step-btn">↓ 1</button>
-            <button class="step-btn">↓ 10</button>
-          </div>
-
-          <div class="extruder-col">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-img"><img src="icons/dual-extruder.png"></div>
-            <span class="extruder-label">Extruder</span>
-            <div class="ext-btns">
-              <button class="ext-btn">▲</button>
-              <button class="ext-btn">▼</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <!-- Nozzle Color Boxes -->
-          <div class="nozzle-boxes">
-            <div class="nozzle-box active">
-              <div class="nozzle-dots">
-                <div class="n-dot" style="background: #FFD700;"></div>
-                <div class="n-dot" style="background: #8B4513;"></div>
-                <div class="n-dot" style="background: #1a1a1a;"></div>
-                <div class="n-dot" style="background: #222;"></div>
-              </div>
-            </div>
-            <div class="nozzle-box">
-              <div class="nozzle-dots">
-                <div class="n-dot" style="background: #0066FF;"></div>
-                <div class="n-dot" style="background: #FF0000;"></div>
-              </div>
-              <div class="nozzle-box-spacer"></div>
-              <div class="nozzle-box-label">0</div>
-            </div>
-            <div class="nozzle-box">
-              <div class="nozzle-dots">
-                <div class="n-dot empty"></div>
-                <div class="n-dot empty"></div>
-                <div class="n-dot empty"></div>
-              </div>
-              <div class="nozzle-box-spacer"></div>
-              <div class="nozzle-box-label" style="background: var(--accent); color: white;">0</div>
-            </div>
-          </div>
-
-          <!-- AMS Content -->
-          <div class="ams-content">
-            <!-- Info Row -->
-            <div class="ams-info-row">
-              <div class="ams-humidity"><img src="icons/water.svg"> 18 %</div>
-              <div class="ams-humidity">☀</div>
-            </div>
-
-            <!-- Slot Labels -->
-            <div class="slot-labels">
-              <div class="slot-label">A1↓</div>
-              <div class="slot-label">A2↓</div>
-              <div class="slot-label">A3↓</div>
-              <div class="slot-label">A4↓</div>
-              <div style="flex: 1;"></div>
-              <div class="slot-label">Ext</div>
-            </div>
-
-            <!-- AMS Grid -->
-            <div class="ams-grid">
-              <!-- Slots -->
-              <div class="ams-slots">
-                <div class="ams-slot active">
-                  <div class="ams-slot-color" style="background: #FFD700;">
-                    <img src="icons/eye.svg">
-                  </div>
-                  <div class="ams-slot-bottom">
-                    <div class="ams-slot-type">PLA</div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-color" style="background: #1a1a1a;">
-                    <img src="icons/eye.svg" style="filter: invert(1);">
-                  </div>
-                  <div class="ams-slot-bottom">
-                    <div class="ams-slot-type">PETG</div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-color" style="background: #8B4513;">
-                    <img src="icons/eye.svg" style="filter: invert(1);">
-                  </div>
-                  <div class="ams-slot-bottom">
-                    <div class="ams-slot-type">PETG</div>
-                  </div>
-                </div>
-                <div class="ams-slot">
-                  <div class="ams-slot-color" style="background: #222;">
-                    <img src="icons/eye.svg" style="filter: invert(1);">
-                  </div>
-                  <div class="ams-slot-bottom">
-                    <div class="ams-slot-type">PLA</div>
-                  </div>
-                </div>
-              </div>
-
-              <!-- External -->
-              <div class="ext-section">
-                <div class="ext-slot-box">
-                  ?
-                  <span class="ext-slot-symbol">∠</span>
-                </div>
-                <div class="spool-holder-img">
-                  <img src="icons/single-extruder1.png">
-                </div>
-              </div>
-            </div>
-
-            <!-- Connectors -->
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"></div>
-                <div class="connector-line"></div>
-                <div class="connector-line"></div>
-                <div class="connector-line"></div>
-              </div>
-            </div>
-
-            <!-- Hub -->
-            <div class="hub-row">
-              <div class="hub-line"></div>
-              <div class="hub-graphic">
-                <div class="hub-dot"></div>
-                <div class="hub-dot"></div>
-              </div>
-              <div class="hub-line"></div>
-            </div>
-          </div>
-
-          <!-- Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">
-              Auto-refill
-              <img src="icons/ams-settings.svg">
-            </button>
-            <div class="ams-spacer"></div>
-            <button class="ams-btn unload">Unload</button>
-            <button class="ams-btn load">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 690
mockup/control-page-v6-multi-ams.html

@@ -1,690 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v6 Multi-AMS</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f0f0f0;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 400px;
-      height: 100vh;
-    }
-
-    /* Left Panel */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      border-top: 1px solid var(--border);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 10px; }
-    .progress-row { display: flex; gap: 14px; align-items: center; }
-    .progress-thumb {
-      width: 54px; height: 54px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .progress-thumb img { width: 32px; opacity: 0.4; }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 13px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 12px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { display: flex; gap: 20px; font-size: 11px; color: var(--text-muted); }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 14px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; }
-    .header-btn {
-      padding: 6px 14px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-
-    .control-body { padding: 14px; }
-
-    /* Temperature */
-    .temp-section { margin-bottom: 14px; }
-    .temp-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
-    .temp-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 18px; opacity: 0.6; }
-    .temp-badge {
-      font-size: 10px; font-weight: 700; color: var(--accent);
-      background: #e8f5e9; padding: 2px 6px; border-radius: 4px;
-    }
-    .temp-value { font-size: 15px; font-weight: 500; }
-    .temp-target { font-size: 14px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex; align-items: center; gap: 12px;
-      padding: 10px 0;
-      border-top: 1px solid var(--border-light);
-      border-bottom: 1px solid var(--border-light);
-      margin-bottom: 14px;
-    }
-    .air-label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-secondary); }
-    .air-label img { width: 16px; opacity: 0.5; }
-    .air-item { display: flex; align-items: center; gap: 6px; padding: 6px 12px; background: var(--bg-light); border-radius: 6px; font-size: 12px; }
-    .air-item img { width: 16px; opacity: 0.5; }
-    .lamp-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement */
-    .movement-section { display: flex; gap: 16px; margin-bottom: 16px; }
-    .jog-pad { position: relative; width: 100px; height: 100px; }
-    .jog-ring {
-      width: 100%; height: 100%; border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #e8e8e8 100%);
-      border: 1px solid var(--border); position: relative;
-    }
-    .jog-label { position: absolute; font-size: 10px; color: var(--text-muted); }
-    .jog-label.top { top: 6px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 6px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 6px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 6px; top: 50%; transform: translateY(-50%); }
-    .jog-btn {
-      position: absolute; width: 22px; height: 22px;
-      background: var(--bg-light); border: 1px solid var(--border);
-      border-radius: 4px; cursor: pointer; font-size: 9px; color: var(--text-muted);
-    }
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 18px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 18px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 18px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 18px; top: 50%; transform: translateY(-50%); }
-    .jog-home {
-      position: absolute; top: 50%; left: 50%;
-      transform: translate(-50%, -50%);
-      width: 32px; height: 32px; border-radius: 50%;
-      background: var(--accent); border: none; cursor: pointer;
-    }
-    .jog-home img { width: 16px; filter: brightness(0) invert(1); }
-
-    .step-section { display: flex; flex-direction: column; gap: 4px; }
-    .step-row { display: flex; gap: 6px; }
-    .step-btn {
-      padding: 5px 8px; background: var(--bg-light);
-      border: 1px solid var(--border); border-radius: 6px;
-      cursor: pointer; font-size: 10px; color: var(--text-secondary);
-    }
-    .step-btn.active { background: var(--bg-hover); border-color: var(--text-muted); }
-    .step-label { font-size: 10px; color: var(--text-muted); text-align: center; padding: 3px 0; }
-
-    .extruder-section { display: flex; flex-direction: column; align-items: center; gap: 4px; }
-    .nozzle-toggle { display: flex; border-radius: 6px; overflow: hidden; border: 1px solid var(--border); }
-    .nozzle-toggle button {
-      padding: 5px 14px; border: none; background: var(--bg-white);
-      font-size: 10px; cursor: pointer; color: var(--text-secondary);
-    }
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-    .extruder-graphic { height: 70px; }
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 10px; color: var(--text-muted); }
-    .ext-controls { display: flex; flex-direction: column; gap: 3px; }
-    .ext-btn {
-      width: 26px; height: 26px; background: var(--bg-light);
-      border: 1px solid var(--border); border-radius: 6px;
-      cursor: pointer; font-size: 11px; color: var(--text-muted);
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section { border-top: 1px solid var(--border-light); padding-top: 14px; }
-
-    /* Nozzle Filament Boxes */
-    .nozzle-filament-row { display: flex; gap: 8px; margin-bottom: 10px; }
-    .nozzle-filament-box {
-      display: flex; align-items: center; gap: 6px;
-      padding: 8px 10px; background: var(--bg-white);
-      border: 2px solid var(--border); border-radius: 8px; cursor: pointer;
-    }
-    .nozzle-filament-box:hover { border-color: #ccc; }
-    .nozzle-filament-box.active { border-color: var(--accent); }
-    .filament-dots { display: flex; gap: 3px; }
-    .f-dot { width: 16px; height: 16px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.1); }
-    .f-dot.empty { background: #eee !important; border: 1px dashed #ccc; }
-    .f-dot-separator { width: 1px; height: 16px; background: #ccc; margin: 0 2px; }
-    .filament-count { font-size: 11px; color: var(--text-muted); padding: 2px 6px; background: var(--bg-light); border-radius: 4px; margin-left: auto; }
-    .filament-count.active { background: var(--accent); color: white; }
-
-    /* AMS Tabs */
-    .ams-tabs { display: flex; gap: 6px; margin-bottom: 8px; flex-wrap: wrap; }
-    .ams-tab {
-      display: flex; align-items: center; gap: 4px;
-      padding: 5px 8px; background: var(--bg-light);
-      border: 2px solid transparent; border-radius: 6px;
-      cursor: pointer; font-size: 10px; color: var(--text-secondary);
-    }
-    .ams-tab:hover { background: var(--bg-hover); }
-    .ams-tab.active { border-color: var(--accent); }
-    .ams-tab-dots { display: flex; gap: 2px; }
-    .ams-tab-dot { width: 10px; height: 10px; border-radius: 2px; border: 1px solid rgba(0,0,0,0.08); }
-    .ams-tab-label { font-size: 9px; color: var(--text-muted); margin-left: 2px; }
-
-    /* AMS-HT Tab (single slot indicator) */
-    .ams-tab.ams-ht .ams-tab-dots { gap: 0; }
-    .ams-tab.ams-ht .ams-tab-dot { width: 14px; height: 14px; border-radius: 3px; }
-
-    /* AMS Content Panel */
-    .ams-content { background: var(--bg-panel); border-radius: 12px; padding: 12px; }
-
-    /* AMS Info Row */
-    .ams-info-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
-    .ams-humidity { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text-secondary); }
-    .ams-humidity img { width: 14px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 14px; color: #f5a623; }
-
-    /* AMS Main Area */
-    .ams-main { display: flex; gap: 16px; }
-
-    /* AMS Slots Container */
-    .ams-slots-container { flex: 1; }
-
-    /* Slot Labels Row */
-    .slot-labels-row { display: flex; gap: 6px; margin-bottom: 4px; padding-left: 2px; }
-    .slot-label-item { width: 50px; display: flex; align-items: center; justify-content: center; }
-    .slot-label-badge {
-      display: flex; align-items: center; gap: 2px;
-      padding: 2px 5px; background: var(--bg-white);
-      border: 1px solid var(--border); border-radius: 10px;
-      font-size: 9px; color: var(--text-muted);
-    }
-    .slot-label-badge .arrow { font-size: 7px; }
-
-    /* AMS Slots */
-    .ams-slots { display: flex; gap: 6px; }
-    .ams-slot {
-      width: 50px; height: 72px;
-      border-radius: 8px; overflow: hidden;
-      cursor: pointer; border: 2px solid var(--border);
-    }
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-    .ams-slot.selected { border-color: var(--accent); }
-    .ams-slot-fill {
-      width: 100%; height: 100%;
-      display: flex; flex-direction: column;
-      align-items: center; justify-content: flex-end;
-      padding-bottom: 6px;
-    }
-    .ams-slot-type { font-size: 11px; font-weight: 600; color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.3); margin-bottom: 2px; }
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-    .ams-slot-eye { width: 18px; height: 18px; }
-    .ams-slot-eye img { width: 14px; height: 14px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-    .ams-slot-empty {
-      width: 100%; height: 100%;
-      background: var(--bg-white);
-      display: flex; flex-direction: column;
-      align-items: center; justify-content: center;
-      color: var(--text-muted);
-    }
-    .ams-slot-empty .question { font-size: 16px; }
-    .ams-slot-empty .angle { font-size: 12px; margin-top: 2px; }
-
-    /* Connector Lines */
-    .ams-connectors { display: flex; padding-left: 2px; margin-top: 4px; }
-    .connector-lines { display: flex; gap: 6px; position: relative; }
-    .connector-line { width: 50px; display: flex; justify-content: center; }
-    .connector-line .vline { width: 2px; height: 12px; background: #c0c0c0; }
-    .connector-hbar { position: absolute; bottom: 0; left: 25px; right: 25px; height: 2px; background: #c0c0c0; }
-
-    /* Hub Row */
-    .hub-row { display: flex; align-items: center; padding-left: 2px; margin-top: -1px; }
-    .hub-line-left { width: calc(100px + 3px); height: 2px; background: #c0c0c0; }
-    .hub-graphic { display: flex; padding: 3px 5px; background: var(--bg-white); border: 1px solid var(--border); border-radius: 3px; }
-    .hub-port { width: 8px; height: 12px; background: var(--accent); border-radius: 2px; margin: 0 1px; }
-    .hub-line-right { width: 30px; height: 2px; background: #c0c0c0; }
-
-    /* External Spool Section */
-    .ext-spool-section { display: flex; flex-direction: column; align-items: center; min-width: 90px; }
-    .ext-label { font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
-    .ext-content { display: flex; gap: 6px; align-items: flex-start; }
-    .ext-slot {
-      width: 50px; height: 72px;
-      background: var(--bg-white); border: 2px solid var(--border);
-      border-radius: 8px; display: flex; flex-direction: column;
-      align-items: center; justify-content: center;
-      cursor: pointer; color: var(--text-muted);
-    }
-    .ext-slot:hover { border-color: #bbb; }
-    .ext-slot .question { font-size: 18px; }
-    .ext-slot .angle { font-size: 12px; margin-top: 2px; }
-    .ext-connector { width: 2px; height: 16px; background: #c0c0c0; margin-top: 4px; }
-    .spool-holder-graphic { width: 70px; height: 90px; }
-    .spool-holder-graphic img { width: 100%; height: 100%; object-fit: contain; }
-
-    /* AMS Actions */
-    .ams-actions { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
-    .auto-refill-btn {
-      display: flex; align-items: center; gap: 5px;
-      padding: 8px 12px; background: var(--bg-white);
-      border: 1px solid var(--border); border-radius: 6px;
-      cursor: pointer; font-size: 12px; color: var(--text-secondary);
-    }
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn {
-      width: 32px; height: 32px;
-      background: var(--bg-white); border: 1px solid var(--border);
-      border-radius: 6px; cursor: pointer;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 18px; opacity: 0.5; }
-    .ams-spacer { flex: 1; }
-    .ams-action-btn {
-      padding: 8px 20px; border-radius: 6px;
-      font-size: 12px; cursor: pointer; border: none;
-      background: #e0e0e0; color: var(--text-secondary);
-    }
-    .ams-action-btn:hover { background: #d5d5d5; }
-
-    /* AMS-HT Specific Styles */
-    .ams-ht-content { padding: 10px; }
-    .ams-ht-slot {
-      width: 60px; height: 80px;
-      border-radius: 10px; overflow: hidden;
-      cursor: pointer; border: 2px solid var(--border);
-      margin: 0 auto;
-    }
-    .ams-ht-label { font-size: 10px; color: var(--text-muted); text-align: center; margin-top: 4px; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb"><img src="icons/micro-sd.svg"></div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta"><span>Layer: N/A</span><span>N/A</span></div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Temperature -->
-        <div class="temp-section">
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/chamber.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-        </div>
-
-        <!-- Air Condition -->
-        <div class="air-section">
-          <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-          <div class="air-item"><img src="icons/ventilation.svg"> 100%</div>
-          <div class="air-item">Lamp <div class="lamp-dot"></div></div>
-        </div>
-
-        <!-- Movement -->
-        <div class="movement-section">
-          <div class="jog-pad">
-            <div class="jog-ring">
-              <span class="jog-label top">Y</span>
-              <span class="jog-label bottom">-Y</span>
-              <span class="jog-label left">-X</span>
-              <span class="jog-label right">X</span>
-              <button class="jog-btn up">&#9650;</button>
-              <button class="jog-btn down">&#9660;</button>
-              <button class="jog-btn left">&#9664;</button>
-              <button class="jog-btn right">&#9654;</button>
-              <button class="jog-home"><img src="icons/home.svg"></button>
-            </div>
-          </div>
-
-          <div class="step-section">
-            <div class="step-row">
-              <button class="step-btn">&#8593; 10</button>
-              <button class="step-btn active">&#8593; 1</button>
-            </div>
-            <div class="step-label">Bed</div>
-            <div class="step-row">
-              <button class="step-btn">&#8595; 1</button>
-              <button class="step-btn">&#8595; 10</button>
-            </div>
-          </div>
-
-          <div class="extruder-section">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-            <span class="extruder-label">Extruder</span>
-            <div class="ext-controls">
-              <button class="ext-btn">&#9650;</button>
-              <button class="ext-btn">&#9660;</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <!-- Nozzle Filament Assignment Boxes -->
-          <div class="nozzle-filament-row">
-            <div class="nozzle-filament-box active">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FFD700;"></div>
-                <div class="f-dot" style="background: #8B4513;"></div>
-                <div class="f-dot" style="background: #1a1a1a;"></div>
-                <div class="f-dot" style="background: #222;"></div>
-              </div>
-            </div>
-            <div class="nozzle-filament-box">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FF6600;"></div>
-                <div class="f-dot" style="background: #00AA00;"></div>
-                <div class="f-dot" style="background: #0066FF;"></div>
-                <div class="f-dot" style="background: #FF0000;"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Tabs - Shows all connected AMS/AMS-HT units -->
-          <div class="ams-tabs">
-            <!-- AMS 1 (Regular AMS - 4 slots) -->
-            <div class="ams-tab active">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FFD700;"></div>
-                <div class="ams-tab-dot" style="background: #1a1a1a;"></div>
-                <div class="ams-tab-dot" style="background: #8B4513;"></div>
-                <div class="ams-tab-dot" style="background: #222;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 1</span>
-            </div>
-            <!-- AMS 2 (Regular AMS - 4 slots) -->
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FF6600;"></div>
-                <div class="ams-tab-dot" style="background: #00AA00;"></div>
-                <div class="ams-tab-dot" style="background: #0066FF;"></div>
-                <div class="ams-tab-dot" style="background: #FF0000;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 2</span>
-            </div>
-            <!-- AMS 3 (Regular AMS - 4 slots) -->
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FFFFFF; border-color: #ccc;"></div>
-                <div class="ams-tab-dot" style="background: #333;"></div>
-                <div class="ams-tab-dot" style="background: #666;"></div>
-                <div class="ams-tab-dot" style="background: #999;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 3</span>
-            </div>
-            <!-- AMS-HT 1 (High Temp - single slot) -->
-            <div class="ams-tab ams-ht">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #8844AA;"></div>
-              </div>
-              <span class="ams-tab-label">HT 1</span>
-            </div>
-            <!-- AMS-HT 2 (High Temp - single slot) -->
-            <div class="ams-tab ams-ht">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #44AA88;"></div>
-              </div>
-              <span class="ams-tab-label">HT 2</span>
-            </div>
-          </div>
-
-          <!-- AMS Content Panel -->
-          <div class="ams-content">
-            <!-- Info Row -->
-            <div class="ams-info-row">
-              <div class="ams-humidity"><img src="icons/water.svg"> 18 %</div>
-              <div class="ams-humidity"><span class="sun">&#9788;</span></div>
-            </div>
-
-            <!-- Main AMS Area -->
-            <div class="ams-main">
-              <!-- AMS Slots Container -->
-              <div class="ams-slots-container">
-                <!-- Slot Labels -->
-                <div class="slot-labels-row">
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A1 <span class="arrow">&#8595;</span></div>
-                  </div>
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A2 <span class="arrow">&#8595;</span></div>
-                  </div>
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A3 <span class="arrow">&#8595;</span></div>
-                  </div>
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A4 <span class="arrow">&#8595;</span></div>
-                  </div>
-                </div>
-
-                <!-- Slots -->
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #1a1a1a;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Connectors -->
-                <div class="ams-connectors">
-                  <div class="connector-lines">
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-hbar"></div>
-                  </div>
-                </div>
-
-                <!-- Hub -->
-                <div class="hub-row">
-                  <div class="hub-line-left"></div>
-                  <div class="hub-graphic">
-                    <div class="hub-port"></div>
-                    <div class="hub-port"></div>
-                  </div>
-                  <div class="hub-line-right"></div>
-                </div>
-              </div>
-
-              <!-- External Spool Section -->
-              <div class="ext-spool-section">
-                <div class="ext-label">Ext</div>
-                <div class="ext-content">
-                  <div style="display: flex; flex-direction: column; align-items: center;">
-                    <div class="ext-slot">
-                      <span class="question">?</span>
-                      <span class="angle">&#8736;</span>
-                    </div>
-                    <div class="ext-connector"></div>
-                  </div>
-                  <div class="spool-holder-graphic">
-                    <img src="icons/single-extruder1.png">
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 1125
mockup/control-page-v6.html

@@ -1,1125 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v6 Exact Match</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f0f0f0;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 380px;
-      height: 100vh;
-    }
-
-    /* Left Panel */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-muted);
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      border-top: 1px solid var(--border);
-      padding: 16px 20px;
-    }
-
-    .progress-label {
-      font-size: 12px;
-      color: var(--text-muted);
-      margin-bottom: 10px;
-    }
-
-    .progress-row {
-      display: flex;
-      gap: 14px;
-      align-items: center;
-    }
-
-    .progress-thumb {
-      width: 54px;
-      height: 54px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .progress-thumb img { width: 32px; opacity: 0.4; }
-
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 13px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 12px; margin-bottom: 8px; }
-
-    .progress-bar-bg {
-      height: 4px;
-      background: var(--border-light);
-      border-radius: 2px;
-      margin-bottom: 8px;
-    }
-
-    .progress-bar-fill {
-      height: 100%;
-      background: var(--accent);
-      border-radius: 2px;
-      width: 0%;
-    }
-
-    .progress-meta {
-      display: flex;
-      gap: 20px;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-
-    .progress-btns {
-      display: flex;
-      gap: 6px;
-    }
-
-    .progress-btn {
-      width: 32px;
-      height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 14px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title {
-      font-size: 13px;
-      margin-right: auto;
-    }
-
-    .header-btn {
-      padding: 6px 14px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-
-    .header-btn:hover { background: #009938; }
-
-    .control-body {
-      padding: 14px;
-    }
-
-    /* Temperature Section */
-    .temp-section {
-      margin-bottom: 14px;
-    }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 4px 0;
-    }
-
-    .temp-icon {
-      width: 20px;
-      height: 20px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-    .temp-icon img { width: 18px; opacity: 0.6; }
-
-    .temp-badge {
-      font-size: 10px;
-      font-weight: 700;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 6px;
-      border-radius: 4px;
-      min-width: 18px;
-      text-align: center;
-    }
-
-    .temp-value { font-size: 15px; font-weight: 500; }
-    .temp-target { font-size: 14px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 10px 0;
-      border-top: 1px solid var(--border-light);
-      border-bottom: 1px solid var(--border-light);
-      margin-bottom: 14px;
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 16px; opacity: 0.5; }
-
-    .air-item {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 6px 12px;
-      background: var(--bg-light);
-      border-radius: 6px;
-      font-size: 12px;
-    }
-
-    .air-item img { width: 16px; opacity: 0.5; }
-    .lamp-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Section */
-    .movement-section {
-      display: flex;
-      gap: 16px;
-      margin-bottom: 16px;
-    }
-
-    .jog-container {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .jog-pad {
-      position: relative;
-      width: 110px;
-      height: 110px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #e8e8e8 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 10px;
-      color: var(--text-muted);
-      font-weight: 500;
-    }
-
-    .jog-label.top { top: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 8px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 8px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 8px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 24px;
-      height: 24px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 10px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
-    .jog-btn.up { top: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 20px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 20px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 20px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 18px; filter: brightness(0) invert(1); }
-
-    /* Step Buttons */
-    .step-section {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-    }
-
-    .step-row {
-      display: flex;
-      gap: 8px;
-      align-items: center;
-    }
-
-    .step-btn {
-      padding: 6px 10px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 11px;
-      color: var(--text-secondary);
-      min-width: 44px;
-      text-align: center;
-    }
-
-    .step-btn:hover { background: var(--bg-hover); }
-    .step-btn.active { background: var(--bg-hover); border-color: var(--text-muted); }
-
-    .step-label {
-      font-size: 11px;
-      color: var(--text-muted);
-      text-align: center;
-      padding: 4px 0;
-    }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-    }
-
-    .nozzle-toggle button {
-      padding: 6px 16px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 11px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .extruder-graphic {
-      height: 80px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-graphic img { height: 100%; }
-
-    .extruder-label { font-size: 11px; color: var(--text-muted); }
-
-    .ext-controls {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-    }
-
-    .ext-btn {
-      width: 28px;
-      height: 28px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ext-btn:hover { background: var(--bg-hover); }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      border-top: 1px solid var(--border-light);
-      padding-top: 14px;
-    }
-
-    /* Nozzle Filament Boxes */
-    .nozzle-filament-row {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 12px;
-    }
-
-    .nozzle-filament-box {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-    }
-
-    .nozzle-filament-box:hover { border-color: #ccc; }
-    .nozzle-filament-box.active { border-color: var(--accent); }
-
-    .filament-dots {
-      display: flex;
-      gap: 4px;
-    }
-
-    .f-dot {
-      width: 18px;
-      height: 18px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .f-dot.empty {
-      background: #eee !important;
-      border: 1px dashed #ccc;
-    }
-
-    .f-dot-separator {
-      width: 1px;
-      height: 18px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .filament-count {
-      font-size: 12px;
-      color: var(--text-muted);
-      padding: 2px 8px;
-      background: var(--bg-light);
-      border-radius: 4px;
-      margin-left: auto;
-    }
-
-    .filament-count.active {
-      background: var(--accent);
-      color: white;
-    }
-
-    /* AMS Tabs (for multiple AMS units) */
-    .ams-tabs {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 8px;
-    }
-
-    .ams-tab {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      padding: 6px 10px;
-      background: var(--bg-light);
-      border: 2px solid transparent;
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    .ams-tab:hover { background: var(--bg-hover); }
-    .ams-tab.active { border-color: var(--accent); }
-
-    .ams-tab-dots {
-      display: flex;
-      gap: 2px;
-    }
-
-    .ams-tab-dot {
-      width: 10px;
-      height: 10px;
-      border-radius: 2px;
-      border: 1px solid rgba(0,0,0,0.08);
-    }
-
-    /* AMS Content Panel */
-    .ams-content {
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 14px;
-    }
-
-    /* AMS Info Row */
-    .ams-info-row {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-bottom: 10px;
-    }
-
-    .ams-humidity {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-humidity img { width: 16px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 16px; color: #f5a623; }
-
-    /* AMS Main Area */
-    .ams-main {
-      display: flex;
-      gap: 20px;
-    }
-
-    /* AMS Slots Container */
-    .ams-slots-container {
-      flex: 1;
-    }
-
-    /* Slot Labels Row */
-    .slot-labels-row {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 6px;
-      padding-left: 4px;
-    }
-
-    .slot-label-item {
-      width: 56px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .slot-label-badge {
-      display: flex;
-      align-items: center;
-      gap: 2px;
-      padding: 3px 6px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 12px;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .slot-label-badge .arrow { font-size: 8px; }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 8px;
-    }
-
-    .ams-slot {
-      width: 56px;
-      height: 80px;
-      border-radius: 10px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      display: flex;
-      flex-direction: column;
-      position: relative;
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-    .ams-slot.selected { border-color: var(--accent); }
-
-    .ams-slot-fill {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 8px;
-      position: relative;
-    }
-
-    .ams-slot-type {
-      font-size: 12px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 4px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye {
-      width: 20px;
-      height: 20px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-slot-eye img {
-      width: 16px;
-      height: 16px;
-      opacity: 0.8;
-    }
-
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    .ams-slot-empty {
-      flex: 1;
-      background: var(--bg-white);
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-muted);
-      font-size: 18px;
-    }
-
-    .ams-slot-empty .angle { font-size: 14px; margin-top: 4px; }
-
-    /* Connector Lines */
-    .ams-connectors {
-      display: flex;
-      justify-content: flex-start;
-      padding: 0 4px;
-      margin-top: 6px;
-    }
-
-    .connector-lines {
-      display: flex;
-      gap: 8px;
-      position: relative;
-    }
-
-    .connector-line {
-      width: 56px;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 16px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 28px;
-      right: 28px;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* Hub Row */
-    .hub-row {
-      display: flex;
-      align-items: center;
-      justify-content: flex-start;
-      padding: 0 4px;
-      margin-top: -1px;
-    }
-
-    .hub-line-left {
-      width: calc(112px + 4px);
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    .hub-graphic {
-      display: flex;
-      align-items: center;
-      padding: 4px 6px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-    }
-
-    .hub-port {
-      width: 10px;
-      height: 14px;
-      background: var(--accent);
-      border-radius: 2px;
-      margin: 0 2px;
-    }
-
-    .hub-line-right {
-      width: 40px;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* External Spool Section */
-    .ext-spool-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      min-width: 100px;
-    }
-
-    .ext-label {
-      font-size: 13px;
-      color: var(--text-secondary);
-      margin-bottom: 6px;
-    }
-
-    .ext-content {
-      display: flex;
-      gap: 8px;
-      align-items: flex-start;
-    }
-
-    .ext-slot {
-      width: 56px;
-      height: 80px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 10px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      cursor: pointer;
-      color: var(--text-muted);
-    }
-
-    .ext-slot:hover { border-color: #bbb; }
-    .ext-slot .question { font-size: 20px; }
-    .ext-slot .angle { font-size: 14px; margin-top: 4px; }
-
-    .ext-connector {
-      width: 2px;
-      height: 22px;
-      background: #c0c0c0;
-      margin-top: 6px;
-    }
-
-    .spool-holder-graphic {
-      width: 80px;
-      height: 100px;
-    }
-
-    .spool-holder-graphic img {
-      width: 100%;
-      height: 100%;
-      object-fit: contain;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      margin-top: 12px;
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 10px 16px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 36px;
-      height: 36px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 20px; opacity: 0.5; }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 10px 24px;
-      border-radius: 8px;
-      font-size: 13px;
-      cursor: pointer;
-      border: none;
-      background: #e0e0e0;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #d5d5d5; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb"><img src="icons/micro-sd.svg"></div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">
-              <span>Layer: N/A</span>
-              <span>N/A</span>
-            </div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Temperature -->
-        <div class="temp-section">
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/chamber.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-        </div>
-
-        <!-- Air Condition -->
-        <div class="air-section">
-          <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-          <div class="air-item"><img src="icons/ventilation.svg"> 100%</div>
-          <div class="air-item">Lamp <div class="lamp-dot"></div></div>
-        </div>
-
-        <!-- Movement -->
-        <div class="movement-section">
-          <div class="jog-container">
-            <div class="jog-pad">
-              <div class="jog-ring">
-                <span class="jog-label top">Y</span>
-                <span class="jog-label bottom">-Y</span>
-                <span class="jog-label left">-X</span>
-                <span class="jog-label right">X</span>
-                <button class="jog-btn up">&#9650;</button>
-                <button class="jog-btn down">&#9660;</button>
-                <button class="jog-btn left">&#9664;</button>
-                <button class="jog-btn right">&#9654;</button>
-                <button class="jog-home"><img src="icons/home.svg"></button>
-              </div>
-            </div>
-          </div>
-
-          <div class="step-section">
-            <div class="step-row">
-              <button class="step-btn">&#8593; 10</button>
-              <button class="step-btn active">&#8593; 1</button>
-            </div>
-            <div class="step-label">Bed</div>
-            <div class="step-row">
-              <button class="step-btn">&#8595; 1</button>
-              <button class="step-btn">&#8595; 10</button>
-            </div>
-          </div>
-
-          <div class="extruder-section">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-            <span class="extruder-label">Extruder</span>
-            <div class="ext-controls">
-              <button class="ext-btn">&#9650;</button>
-              <button class="ext-btn">&#9660;</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <!-- Nozzle Filament Assignment Boxes -->
-          <div class="nozzle-filament-row">
-            <div class="nozzle-filament-box active">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FFD700;"></div>
-                <div class="f-dot" style="background: #8B4513;"></div>
-                <div class="f-dot" style="background: #1a1a1a;"></div>
-                <div class="f-dot" style="background: #222;"></div>
-              </div>
-            </div>
-            <div class="nozzle-filament-box">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #1a1a1a;"></div>
-                <div class="f-dot" style="background: #1a1a1a;"></div>
-                <div class="f-dot-separator"></div>
-                <div class="f-dot" style="background: #FF0000;"></div>
-              </div>
-              <span class="filament-count">0</span>
-            </div>
-            <div class="nozzle-filament-box">
-              <div class="filament-dots">
-                <div class="f-dot empty"></div>
-                <div class="f-dot empty"></div>
-                <div class="f-dot empty"></div>
-              </div>
-              <span class="filament-count active">0</span>
-            </div>
-          </div>
-
-          <!-- AMS Content Panel -->
-          <div class="ams-content">
-            <!-- Info Row -->
-            <div class="ams-info-row">
-              <div class="ams-humidity"><img src="icons/water.svg"> 18 %</div>
-              <div class="ams-humidity"><span class="sun">&#9788;</span></div>
-            </div>
-
-            <!-- Main AMS Area -->
-            <div class="ams-main">
-              <!-- AMS Slots Container -->
-              <div class="ams-slots-container">
-                <!-- Slot Labels -->
-                <div class="slot-labels-row">
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A1 <span class="arrow">&#8595;</span></div>
-                  </div>
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A2 <span class="arrow">&#8595;</span></div>
-                  </div>
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A3 <span class="arrow">&#8595;</span></div>
-                  </div>
-                  <div class="slot-label-item">
-                    <div class="slot-label-badge">A4 <span class="arrow">&#8595;</span></div>
-                  </div>
-                </div>
-
-                <!-- Slots -->
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #1a1a1a;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <!-- Connectors -->
-                <div class="ams-connectors">
-                  <div class="connector-lines">
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-hbar"></div>
-                  </div>
-                </div>
-
-                <!-- Hub -->
-                <div class="hub-row">
-                  <div class="hub-line-left"></div>
-                  <div class="hub-graphic">
-                    <div class="hub-port"></div>
-                    <div class="hub-port"></div>
-                  </div>
-                  <div class="hub-line-right"></div>
-                </div>
-              </div>
-
-              <!-- External Spool Section -->
-              <div class="ext-spool-section">
-                <div class="ext-label">Ext</div>
-                <div class="ext-content">
-                  <div style="display: flex; flex-direction: column; align-items: center;">
-                    <div class="ext-slot">
-                      <span class="question">?</span>
-                      <span class="angle">&#8736;</span>
-                    </div>
-                    <div class="ext-connector"></div>
-                  </div>
-                  <div class="spool-holder-graphic">
-                    <img src="icons/single-extruder1.png">
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Actions -->
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 654
mockup/control-page-v7.html

@@ -1,654 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v7</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f0f0f0;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 300px;
-      height: 100vh;
-    }
-
-    /* Left Panel */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 6px 10px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 6px;
-    }
-
-    .camera-title {
-      font-size: 12px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 24px;
-      height: 24px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 16px; height: 16px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      border-top: 1px solid var(--border);
-      padding: 12px 16px;
-    }
-
-    .progress-label { font-size: 11px; color: var(--text-muted); margin-bottom: 8px; }
-    .progress-row { display: flex; gap: 12px; align-items: center; }
-    .progress-thumb {
-      width: 48px; height: 48px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .progress-thumb img { width: 28px; opacity: 0.4; }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 13px; }
-    .progress-status { color: var(--accent); font-size: 12px; margin: 2px 0 6px; }
-    .progress-bar-bg { height: 3px; background: var(--border-light); border-radius: 2px; margin-bottom: 6px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 11px; color: var(--text-muted); }
-    .progress-btns { display: flex; gap: 4px; }
-    .progress-btn {
-      width: 28px; height: 28px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 10px;
-      border-bottom: 1px solid var(--border);
-      gap: 6px;
-    }
-
-    .control-title { font-size: 12px; margin-right: auto; }
-    .header-btn {
-      padding: 5px 10px;
-      font-size: 11px;
-      border: none;
-      border-radius: 4px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-
-    .control-body { padding: 10px; flex: 1; overflow-y: auto; }
-
-    /* Temperature - Compact */
-    .temp-section { margin-bottom: 10px; }
-    .temp-row { display: flex; align-items: center; gap: 6px; padding: 2px 0; }
-    .temp-icon { width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 14px; opacity: 0.6; }
-    .temp-badge {
-      font-size: 9px; font-weight: 700; color: var(--accent);
-      background: #e8f5e9; padding: 1px 4px; border-radius: 3px;
-    }
-    .temp-value { font-size: 14px; font-weight: 500; }
-    .temp-target { font-size: 12px; color: var(--text-muted); }
-
-    /* Air Condition - Compact */
-    .air-section {
-      display: flex; align-items: center; gap: 8px;
-      padding: 8px 0;
-      border-top: 1px solid var(--border-light);
-      border-bottom: 1px solid var(--border-light);
-      margin-bottom: 10px;
-    }
-    .air-label { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-secondary); }
-    .air-label img { width: 14px; opacity: 0.5; }
-    .air-item { display: flex; align-items: center; gap: 4px; padding: 4px 8px; background: var(--bg-light); border-radius: 4px; font-size: 11px; }
-    .air-item img { width: 14px; opacity: 0.5; }
-    .lamp-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement - Compact */
-    .movement-section { display: flex; gap: 10px; margin-bottom: 12px; align-items: flex-start; }
-
-    .jog-pad { position: relative; width: 90px; height: 90px; }
-    .jog-ring {
-      width: 100%; height: 100%; border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #e8e8e8 100%);
-      border: 1px solid var(--border); position: relative;
-    }
-    .jog-label { position: absolute; font-size: 9px; color: var(--text-muted); }
-    .jog-label.top { top: 5px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 5px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 5px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 5px; top: 50%; transform: translateY(-50%); }
-    .jog-btn {
-      position: absolute; width: 18px; height: 18px;
-      background: var(--bg-light); border: 1px solid var(--border);
-      border-radius: 3px; cursor: pointer; font-size: 8px; color: var(--text-muted);
-      display: flex; align-items: center; justify-content: center;
-    }
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 16px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 16px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 16px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 16px; top: 50%; transform: translateY(-50%); }
-    .jog-home {
-      position: absolute; top: 50%; left: 50%;
-      transform: translate(-50%, -50%);
-      width: 28px; height: 28px; border-radius: 50%;
-      background: var(--accent); border: none; cursor: pointer;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .jog-home img { width: 14px; filter: brightness(0) invert(1); }
-
-    .step-section { display: flex; flex-direction: column; gap: 2px; }
-    .step-row { display: flex; gap: 4px; }
-    .step-btn {
-      padding: 4px 6px; background: var(--bg-light);
-      border: 1px solid var(--border); border-radius: 4px;
-      cursor: pointer; font-size: 9px; color: var(--text-secondary);
-    }
-    .step-btn.active { background: var(--bg-hover); border-color: var(--text-muted); }
-    .step-label { font-size: 9px; color: var(--text-muted); text-align: center; padding: 2px 0; }
-
-    .extruder-section { display: flex; flex-direction: column; align-items: center; gap: 3px; }
-    .nozzle-toggle { display: flex; border-radius: 4px; overflow: hidden; border: 1px solid var(--border); }
-    .nozzle-toggle button {
-      padding: 4px 10px; border: none; background: var(--bg-white);
-      font-size: 9px; cursor: pointer; color: var(--text-secondary);
-    }
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-    .extruder-graphic { height: 60px; }
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 9px; color: var(--text-muted); }
-    .ext-controls { display: flex; flex-direction: column; gap: 2px; }
-    .ext-btn {
-      width: 22px; height: 22px; background: var(--bg-light);
-      border: 1px solid var(--border); border-radius: 4px;
-      cursor: pointer; font-size: 10px; color: var(--text-muted);
-      display: flex; align-items: center; justify-content: center;
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section { border-top: 1px solid var(--border-light); padding-top: 10px; }
-
-    /* Nozzle Filament Boxes */
-    .nozzle-filament-row { display: flex; gap: 6px; margin-bottom: 8px; }
-    .nozzle-filament-box {
-      display: flex; align-items: center; gap: 4px;
-      padding: 6px 8px; background: var(--bg-white);
-      border: 2px solid var(--border); border-radius: 6px; cursor: pointer;
-    }
-    .nozzle-filament-box:hover { border-color: #ccc; }
-    .nozzle-filament-box.active { border-color: var(--accent); }
-    .filament-dots { display: flex; gap: 2px; }
-    .f-dot { width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.1); }
-    .f-dot.empty { background: #eee !important; border: 1px dashed #ccc; }
-
-    /* AMS Tabs */
-    .ams-tabs { display: flex; gap: 4px; margin-bottom: 6px; flex-wrap: wrap; }
-    .ams-tab {
-      display: flex; align-items: center; gap: 3px;
-      padding: 4px 6px; background: var(--bg-light);
-      border: 2px solid transparent; border-radius: 5px;
-      cursor: pointer; font-size: 9px; color: var(--text-secondary);
-    }
-    .ams-tab:hover { background: var(--bg-hover); }
-    .ams-tab.active { border-color: var(--accent); }
-    .ams-tab-dots { display: flex; gap: 1px; }
-    .ams-tab-dot { width: 8px; height: 8px; border-radius: 2px; border: 1px solid rgba(0,0,0,0.08); }
-    .ams-tab-label { font-size: 8px; color: var(--text-muted); }
-
-    /* AMS Content Panel */
-    .ams-content { background: var(--bg-panel); border-radius: 10px; padding: 10px; }
-
-    /* AMS Info Row */
-    .ams-info-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
-    .ams-humidity { display: flex; align-items: center; gap: 3px; font-size: 11px; color: var(--text-secondary); }
-    .ams-humidity img { width: 12px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 12px; color: #f5a623; }
-
-    /* AMS Main Area */
-    .ams-main { display: flex; gap: 8px; }
-
-    /* AMS Slots Container */
-    .ams-slots-container { flex: 1; }
-
-    /* Slot Labels Row */
-    .slot-labels-row { display: flex; gap: 4px; margin-bottom: 3px; }
-    .slot-label-item { width: 44px; display: flex; align-items: center; justify-content: center; }
-    .slot-label-badge {
-      display: flex; align-items: center; gap: 1px;
-      padding: 2px 4px; background: var(--bg-white);
-      border: 1px solid var(--border); border-radius: 8px;
-      font-size: 8px; color: var(--text-muted);
-    }
-
-    /* AMS Slots */
-    .ams-slots { display: flex; gap: 4px; }
-    .ams-slot {
-      width: 44px; height: 64px;
-      border-radius: 6px; overflow: hidden;
-      cursor: pointer; border: 2px solid var(--border);
-    }
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-    .ams-slot-fill {
-      width: 100%; height: 100%;
-      display: flex; flex-direction: column;
-      align-items: center; justify-content: flex-end;
-      padding-bottom: 4px;
-    }
-    .ams-slot-type { font-size: 10px; font-weight: 600; color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.3); margin-bottom: 2px; }
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-    .ams-slot-eye { width: 14px; height: 14px; }
-    .ams-slot-eye img { width: 12px; height: 12px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-    .ams-slot-empty {
-      width: 100%; height: 100%;
-      background: var(--bg-white);
-      display: flex; flex-direction: column;
-      align-items: center; justify-content: center;
-      color: var(--text-muted);
-    }
-    .ams-slot-empty .question { font-size: 14px; }
-    .ams-slot-empty .angle { font-size: 10px; }
-
-    /* Connector Lines */
-    .ams-connectors { display: flex; margin-top: 3px; }
-    .connector-lines { display: flex; gap: 4px; position: relative; }
-    .connector-line { width: 44px; display: flex; justify-content: center; }
-    .connector-line .vline { width: 2px; height: 10px; background: #c0c0c0; }
-    .connector-hbar { position: absolute; bottom: 0; left: 22px; right: 22px; height: 2px; background: #c0c0c0; }
-
-    /* Hub Row */
-    .hub-row { display: flex; align-items: center; margin-top: -1px; }
-    .hub-line-left { width: 88px; height: 2px; background: #c0c0c0; }
-    .hub-graphic { display: flex; padding: 2px 4px; background: var(--bg-white); border: 1px solid var(--border); border-radius: 3px; }
-    .hub-port { width: 6px; height: 10px; background: var(--accent); border-radius: 1px; margin: 0 1px; }
-    .hub-line-right { width: 20px; height: 2px; background: #c0c0c0; }
-
-    /* External Spool Section */
-    .ext-spool-section { display: flex; flex-direction: column; align-items: center; }
-    .ext-label { font-size: 10px; color: var(--text-secondary); margin-bottom: 3px; }
-    .ext-content { display: flex; gap: 4px; align-items: flex-start; }
-    .ext-slot {
-      width: 44px; height: 64px;
-      background: var(--bg-white); border: 2px solid var(--border);
-      border-radius: 6px; display: flex; flex-direction: column;
-      align-items: center; justify-content: center;
-      cursor: pointer; color: var(--text-muted);
-    }
-    .ext-slot:hover { border-color: #bbb; }
-    .ext-slot .question { font-size: 14px; }
-    .ext-slot .angle { font-size: 10px; }
-    .ext-connector { width: 2px; height: 12px; background: #c0c0c0; margin-top: 3px; }
-    .spool-holder-graphic { width: 50px; height: 70px; }
-    .spool-holder-graphic img { width: 100%; height: 100%; object-fit: contain; }
-
-    /* AMS Actions */
-    .ams-actions { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
-    .auto-refill-btn {
-      display: flex; align-items: center; gap: 4px;
-      padding: 6px 10px; background: var(--bg-white);
-      border: 1px solid var(--border); border-radius: 5px;
-      cursor: pointer; font-size: 11px; color: var(--text-secondary);
-    }
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn {
-      width: 28px; height: 28px;
-      background: var(--bg-white); border: 1px solid var(--border);
-      border-radius: 5px; cursor: pointer;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 16px; opacity: 0.5; }
-    .ams-spacer { flex: 1; }
-    .ams-action-btn {
-      padding: 6px 16px; border-radius: 5px;
-      font-size: 11px; cursor: pointer; border: none;
-      background: #e0e0e0; color: var(--text-secondary);
-    }
-    .ams-action-btn:hover { background: #d5d5d5; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb"><img src="icons/micro-sd.svg"></div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Temperature -->
-        <div class="temp-section">
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/chamber.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-        </div>
-
-        <!-- Air Condition -->
-        <div class="air-section">
-          <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-          <div class="air-item"><img src="icons/ventilation.svg"> 100%</div>
-          <div class="air-item">Lamp <div class="lamp-dot"></div></div>
-        </div>
-
-        <!-- Movement -->
-        <div class="movement-section">
-          <div class="jog-pad">
-            <div class="jog-ring">
-              <span class="jog-label top">Y</span>
-              <span class="jog-label bottom">-Y</span>
-              <span class="jog-label left">-X</span>
-              <span class="jog-label right">X</span>
-              <button class="jog-btn up">&#9650;</button>
-              <button class="jog-btn down">&#9660;</button>
-              <button class="jog-btn left">&#9664;</button>
-              <button class="jog-btn right">&#9654;</button>
-              <button class="jog-home"><img src="icons/home.svg"></button>
-            </div>
-          </div>
-
-          <div class="step-section">
-            <div class="step-row">
-              <button class="step-btn">&#8593;10</button>
-              <button class="step-btn active">&#8593;1</button>
-            </div>
-            <div class="step-label">Bed</div>
-            <div class="step-row">
-              <button class="step-btn">&#8595;1</button>
-              <button class="step-btn">&#8595;10</button>
-            </div>
-          </div>
-
-          <div class="extruder-section">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-            <span class="extruder-label">Extruder</span>
-            <div class="ext-controls">
-              <button class="ext-btn">&#9650;</button>
-              <button class="ext-btn">&#9660;</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <!-- Nozzle Filament Assignment Boxes -->
-          <div class="nozzle-filament-row">
-            <div class="nozzle-filament-box active">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FFD700;"></div>
-                <div class="f-dot" style="background: #8B4513;"></div>
-                <div class="f-dot" style="background: #1a1a1a;"></div>
-                <div class="f-dot" style="background: #222;"></div>
-              </div>
-            </div>
-            <div class="nozzle-filament-box">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FF6600;"></div>
-                <div class="f-dot" style="background: #00AA00;"></div>
-                <div class="f-dot" style="background: #0066FF;"></div>
-                <div class="f-dot" style="background: #FF0000;"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Tabs -->
-          <div class="ams-tabs">
-            <div class="ams-tab active">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FFD700;"></div>
-                <div class="ams-tab-dot" style="background: #8B4513;"></div>
-                <div class="ams-tab-dot" style="background: #1a1a1a;"></div>
-                <div class="ams-tab-dot" style="background: #222;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 1</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FF6600;"></div>
-                <div class="ams-tab-dot" style="background: #00AA00;"></div>
-                <div class="ams-tab-dot" style="background: #0066FF;"></div>
-                <div class="ams-tab-dot" style="background: #FF0000;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 2</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #fff; border-color: #ccc;"></div>
-                <div class="ams-tab-dot" style="background: #333;"></div>
-                <div class="ams-tab-dot" style="background: #666;"></div>
-                <div class="ams-tab-dot" style="background: #999;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 3</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #8844AA;"></div>
-              </div>
-              <span class="ams-tab-label">HT 1</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #44AA88;"></div>
-              </div>
-              <span class="ams-tab-label">HT 2</span>
-            </div>
-          </div>
-
-          <!-- AMS Content Panel -->
-          <div class="ams-content">
-            <div class="ams-info-row">
-              <div class="ams-humidity"><img src="icons/water.svg"> 18 %</div>
-              <div class="ams-humidity"><span class="sun">&#9788;</span></div>
-            </div>
-
-            <div class="ams-main">
-              <div class="ams-slots-container">
-                <div class="slot-labels-row">
-                  <div class="slot-label-item"><div class="slot-label-badge">A1&#8595;</div></div>
-                  <div class="slot-label-item"><div class="slot-label-badge">A2&#8595;</div></div>
-                  <div class="slot-label-item"><div class="slot-label-badge">A3&#8595;</div></div>
-                  <div class="slot-label-item"><div class="slot-label-badge">A4&#8595;</div></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #1a1a1a;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="ams-connectors">
-                  <div class="connector-lines">
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-hbar"></div>
-                  </div>
-                </div>
-
-                <div class="hub-row">
-                  <div class="hub-line-left"></div>
-                  <div class="hub-graphic">
-                    <div class="hub-port"></div>
-                    <div class="hub-port"></div>
-                  </div>
-                  <div class="hub-line-right"></div>
-                </div>
-              </div>
-
-              <div class="ext-spool-section">
-                <div class="ext-label">Ext</div>
-                <div class="ext-content">
-                  <div style="display: flex; flex-direction: column; align-items: center;">
-                    <div class="ext-slot">
-                      <span class="question">?</span>
-                      <span class="angle">&#8736;</span>
-                    </div>
-                    <div class="ext-connector"></div>
-                  </div>
-                  <div class="spool-holder-graphic">
-                    <img src="icons/single-extruder1.png">
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 652
mockup/control-page-v8.html

@@ -1,652 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v8</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f0f0f0;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 280px;
-      height: 100vh;
-    }
-
-    /* Left Panel */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 6px 10px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 6px;
-    }
-
-    .camera-title {
-      font-size: 12px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 24px;
-      height: 24px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 16px; height: 16px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      border-top: 1px solid var(--border);
-      padding: 10px 14px;
-    }
-
-    .progress-label { font-size: 11px; color: var(--text-muted); margin-bottom: 6px; }
-    .progress-row { display: flex; gap: 10px; align-items: center; }
-    .progress-thumb {
-      width: 44px; height: 44px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .progress-thumb img { width: 26px; opacity: 0.4; }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 12px; }
-    .progress-status { color: var(--accent); font-size: 11px; margin: 2px 0 4px; }
-    .progress-bar-bg { height: 3px; background: var(--border-light); border-radius: 2px; margin-bottom: 4px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 10px; color: var(--text-muted); }
-    .progress-btns { display: flex; gap: 4px; }
-    .progress-btn {
-      width: 26px; height: 26px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 11px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 6px 8px;
-      border-bottom: 1px solid var(--border);
-      gap: 4px;
-    }
-
-    .control-title { font-size: 11px; margin-right: auto; }
-    .header-btn {
-      padding: 4px 8px;
-      font-size: 10px;
-      border: none;
-      border-radius: 4px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-
-    .control-body { padding: 8px; flex: 1; overflow-y: auto; }
-
-    /* Temperature - Compact */
-    .temp-section { margin-bottom: 8px; }
-    .temp-row { display: flex; align-items: center; gap: 4px; padding: 1px 0; }
-    .temp-icon { width: 14px; height: 14px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 12px; opacity: 0.6; }
-    .temp-badge {
-      font-size: 8px; font-weight: 700; color: var(--accent);
-      background: #e8f5e9; padding: 1px 3px; border-radius: 2px;
-    }
-    .temp-value { font-size: 13px; font-weight: 500; }
-    .temp-target { font-size: 11px; color: var(--text-muted); }
-
-    /* Air Condition - Compact */
-    .air-section {
-      display: flex; align-items: center; gap: 6px;
-      padding: 6px 0;
-      border-top: 1px solid var(--border-light);
-      border-bottom: 1px solid var(--border-light);
-      margin-bottom: 8px;
-    }
-    .air-label { display: flex; align-items: center; gap: 3px; font-size: 10px; color: var(--text-secondary); }
-    .air-label img { width: 12px; opacity: 0.5; }
-    .air-item { display: flex; align-items: center; gap: 3px; padding: 3px 6px; background: var(--bg-light); border-radius: 4px; font-size: 10px; }
-    .air-item img { width: 12px; opacity: 0.5; }
-    .lamp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement - Compact */
-    .movement-section { display: flex; gap: 8px; margin-bottom: 10px; align-items: flex-start; }
-
-    .jog-pad { position: relative; width: 80px; height: 80px; }
-    .jog-ring {
-      width: 100%; height: 100%; border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #e8e8e8 100%);
-      border: 1px solid var(--border); position: relative;
-    }
-    .jog-label { position: absolute; font-size: 8px; color: var(--text-muted); }
-    .jog-label.top { top: 4px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 4px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 4px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 4px; top: 50%; transform: translateY(-50%); }
-    .jog-btn {
-      position: absolute; width: 16px; height: 16px;
-      background: var(--bg-light); border: 1px solid var(--border);
-      border-radius: 3px; cursor: pointer; font-size: 7px; color: var(--text-muted);
-      display: flex; align-items: center; justify-content: center;
-    }
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 14px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 14px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 14px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 14px; top: 50%; transform: translateY(-50%); }
-    .jog-home {
-      position: absolute; top: 50%; left: 50%;
-      transform: translate(-50%, -50%);
-      width: 24px; height: 24px; border-radius: 50%;
-      background: var(--accent); border: none; cursor: pointer;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .jog-home img { width: 12px; filter: brightness(0) invert(1); }
-
-    .step-section { display: flex; flex-direction: column; gap: 2px; }
-    .step-row { display: flex; gap: 3px; }
-    .step-btn {
-      padding: 3px 5px; background: var(--bg-light);
-      border: 1px solid var(--border); border-radius: 3px;
-      cursor: pointer; font-size: 8px; color: var(--text-secondary);
-    }
-    .step-btn.active { background: var(--bg-hover); border-color: var(--text-muted); }
-    .step-label { font-size: 8px; color: var(--text-muted); text-align: center; padding: 1px 0; }
-
-    .extruder-section { display: flex; flex-direction: column; align-items: center; gap: 2px; }
-    .nozzle-toggle { display: flex; border-radius: 3px; overflow: hidden; border: 1px solid var(--border); }
-    .nozzle-toggle button {
-      padding: 3px 8px; border: none; background: var(--bg-white);
-      font-size: 8px; cursor: pointer; color: var(--text-secondary);
-    }
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-    .extruder-graphic { height: 50px; }
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 8px; color: var(--text-muted); }
-    .ext-controls { display: flex; flex-direction: column; gap: 2px; }
-    .ext-btn {
-      width: 20px; height: 20px; background: var(--bg-light);
-      border: 1px solid var(--border); border-radius: 3px;
-      cursor: pointer; font-size: 9px; color: var(--text-muted);
-      display: flex; align-items: center; justify-content: center;
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section { border-top: 1px solid var(--border-light); padding-top: 8px; }
-
-    /* Nozzle Filament Boxes */
-    .nozzle-filament-row { display: flex; gap: 4px; margin-bottom: 6px; }
-    .nozzle-filament-box {
-      display: flex; align-items: center; gap: 3px;
-      padding: 5px 6px; background: var(--bg-white);
-      border: 2px solid var(--border); border-radius: 5px; cursor: pointer;
-    }
-    .nozzle-filament-box:hover { border-color: #ccc; }
-    .nozzle-filament-box.active { border-color: var(--accent); }
-    .filament-dots { display: flex; gap: 2px; }
-    .f-dot { width: 12px; height: 12px; border-radius: 2px; border: 1px solid rgba(0,0,0,0.1); }
-
-    /* AMS Tabs */
-    .ams-tabs { display: flex; gap: 3px; margin-bottom: 5px; flex-wrap: wrap; }
-    .ams-tab {
-      display: flex; align-items: center; gap: 2px;
-      padding: 3px 5px; background: var(--bg-light);
-      border: 2px solid transparent; border-radius: 4px;
-      cursor: pointer; font-size: 8px; color: var(--text-secondary);
-    }
-    .ams-tab:hover { background: var(--bg-hover); }
-    .ams-tab.active { border-color: var(--accent); }
-    .ams-tab-dots { display: flex; gap: 1px; }
-    .ams-tab-dot { width: 7px; height: 7px; border-radius: 1px; border: 1px solid rgba(0,0,0,0.08); }
-    .ams-tab-label { font-size: 7px; color: var(--text-muted); }
-
-    /* AMS Content Panel */
-    .ams-content { background: var(--bg-panel); border-radius: 8px; padding: 8px; }
-
-    /* AMS Info Row */
-    .ams-info-row { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
-    .ams-humidity { display: flex; align-items: center; gap: 2px; font-size: 10px; color: var(--text-secondary); }
-    .ams-humidity img { width: 11px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 11px; color: #f5a623; }
-
-    /* AMS Main Area */
-    .ams-main { display: flex; gap: 6px; }
-
-    /* AMS Slots Container */
-    .ams-slots-container { flex: 1; }
-
-    /* Slot Labels Row */
-    .slot-labels-row { display: flex; gap: 3px; margin-bottom: 2px; }
-    .slot-label-item { width: 40px; display: flex; align-items: center; justify-content: center; }
-    .slot-label-badge {
-      display: flex; align-items: center;
-      padding: 1px 3px;
-      font-size: 7px; color: var(--text-muted);
-    }
-
-    /* AMS Slots */
-    .ams-slots { display: flex; gap: 3px; }
-    .ams-slot {
-      width: 40px; height: 56px;
-      border-radius: 5px; overflow: hidden;
-      cursor: pointer; border: 2px solid var(--border);
-    }
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-    .ams-slot-fill {
-      width: 100%; height: 100%;
-      display: flex; flex-direction: column;
-      align-items: center; justify-content: flex-end;
-      padding-bottom: 3px;
-    }
-    .ams-slot-type { font-size: 9px; font-weight: 600; color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.3); margin-bottom: 1px; }
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-    .ams-slot-eye { width: 12px; height: 12px; }
-    .ams-slot-eye img { width: 10px; height: 10px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-    .ams-slot-empty {
-      width: 100%; height: 100%;
-      background: var(--bg-white);
-      display: flex; flex-direction: column;
-      align-items: center; justify-content: center;
-      color: var(--text-muted);
-    }
-    .ams-slot-empty .question { font-size: 12px; }
-    .ams-slot-empty .angle { font-size: 9px; }
-
-    /* Connector Lines */
-    .ams-connectors { display: flex; margin-top: 2px; }
-    .connector-lines { display: flex; gap: 3px; position: relative; }
-    .connector-line { width: 40px; display: flex; justify-content: center; }
-    .connector-line .vline { width: 2px; height: 8px; background: #c0c0c0; }
-    .connector-hbar { position: absolute; bottom: 0; left: 20px; right: 20px; height: 2px; background: #c0c0c0; }
-
-    /* Hub Row */
-    .hub-row { display: flex; align-items: center; margin-top: -1px; }
-    .hub-line-left { width: 78px; height: 2px; background: #c0c0c0; }
-    .hub-graphic { display: flex; padding: 2px 3px; background: var(--bg-white); border: 1px solid var(--border); border-radius: 2px; }
-    .hub-port { width: 5px; height: 8px; background: var(--accent); border-radius: 1px; margin: 0 1px; }
-    .hub-line-right { width: 14px; height: 2px; background: #c0c0c0; }
-
-    /* External Spool Section */
-    .ext-spool-section { display: flex; flex-direction: column; align-items: center; width: 80px; }
-    .ext-label { font-size: 9px; color: var(--text-secondary); margin-bottom: 2px; }
-    .ext-row { display: flex; align-items: flex-start; gap: 4px; }
-    .ext-slot {
-      width: 40px; height: 56px;
-      background: var(--bg-white); border: 2px solid var(--border);
-      border-radius: 5px; display: flex; flex-direction: column;
-      align-items: center; justify-content: center;
-      cursor: pointer; color: var(--text-muted);
-    }
-    .ext-slot:hover { border-color: #bbb; }
-    .ext-slot .question { font-size: 12px; }
-    .ext-slot .angle { font-size: 9px; }
-    .ext-connector { width: 2px; height: 10px; background: #c0c0c0; margin: 2px auto 0; }
-    .spool-holder { width: 36px; height: 56px; display: flex; align-items: center; justify-content: center; }
-    .spool-holder img { width: 100%; height: 100%; object-fit: contain; }
-
-    /* AMS Actions */
-    .ams-actions { display: flex; align-items: center; gap: 4px; margin-top: 6px; }
-    .auto-refill-btn {
-      display: flex; align-items: center; gap: 3px;
-      padding: 5px 8px; background: var(--bg-white);
-      border: 1px solid var(--border); border-radius: 4px;
-      cursor: pointer; font-size: 10px; color: var(--text-secondary);
-    }
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn {
-      width: 24px; height: 24px;
-      background: var(--bg-white); border: 1px solid var(--border);
-      border-radius: 4px; cursor: pointer;
-      display: flex; align-items: center; justify-content: center;
-    }
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 14px; opacity: 0.5; }
-    .ams-spacer { flex: 1; }
-    .ams-action-btn {
-      padding: 5px 12px; border-radius: 4px;
-      font-size: 10px; cursor: pointer; border: none;
-      background: #e0e0e0; color: var(--text-secondary);
-    }
-    .ams-action-btn:hover { background: #d5d5d5; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb"><img src="icons/micro-sd.svg"></div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp; Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Temperature -->
-        <div class="temp-section">
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">L</span>
-            <span class="temp-value">22</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/hotend.svg"></div>
-            <span class="temp-badge">R</span>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-          <div class="temp-row">
-            <div class="temp-icon"><img src="icons/chamber.svg"></div>
-            <span class="temp-value">21</span>
-            <span class="temp-target">/0 °C</span>
-          </div>
-        </div>
-
-        <!-- Air Condition -->
-        <div class="air-section">
-          <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-          <div class="air-item"><img src="icons/ventilation.svg"> 100%</div>
-          <div class="air-item">Lamp <div class="lamp-dot"></div></div>
-        </div>
-
-        <!-- Movement -->
-        <div class="movement-section">
-          <div class="jog-pad">
-            <div class="jog-ring">
-              <span class="jog-label top">Y</span>
-              <span class="jog-label bottom">-Y</span>
-              <span class="jog-label left">-X</span>
-              <span class="jog-label right">X</span>
-              <button class="jog-btn up">&#9650;</button>
-              <button class="jog-btn down">&#9660;</button>
-              <button class="jog-btn left">&#9664;</button>
-              <button class="jog-btn right">&#9654;</button>
-              <button class="jog-home"><img src="icons/home.svg"></button>
-            </div>
-          </div>
-
-          <div class="step-section">
-            <div class="step-row">
-              <button class="step-btn">&#8593;10</button>
-              <button class="step-btn active">&#8593;1</button>
-            </div>
-            <div class="step-label">Bed</div>
-            <div class="step-row">
-              <button class="step-btn">&#8595;1</button>
-              <button class="step-btn">&#8595;10</button>
-            </div>
-          </div>
-
-          <div class="extruder-section">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-            <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-            <span class="extruder-label">Extruder</span>
-            <div class="ext-controls">
-              <button class="ext-btn">&#9650;</button>
-              <button class="ext-btn">&#9660;</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Section -->
-        <div class="ams-section">
-          <!-- Nozzle Filament Boxes -->
-          <div class="nozzle-filament-row">
-            <div class="nozzle-filament-box active">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FFD700;"></div>
-                <div class="f-dot" style="background: #8B4513;"></div>
-                <div class="f-dot" style="background: #1a1a1a;"></div>
-                <div class="f-dot" style="background: #222;"></div>
-              </div>
-            </div>
-            <div class="nozzle-filament-box">
-              <div class="filament-dots">
-                <div class="f-dot" style="background: #FF6600;"></div>
-                <div class="f-dot" style="background: #00AA00;"></div>
-                <div class="f-dot" style="background: #0066FF;"></div>
-                <div class="f-dot" style="background: #FF0000;"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Tabs -->
-          <div class="ams-tabs">
-            <div class="ams-tab active">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FFD700;"></div>
-                <div class="ams-tab-dot" style="background: #8B4513;"></div>
-                <div class="ams-tab-dot" style="background: #1a1a1a;"></div>
-                <div class="ams-tab-dot" style="background: #222;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 1</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #FF6600;"></div>
-                <div class="ams-tab-dot" style="background: #00AA00;"></div>
-                <div class="ams-tab-dot" style="background: #0066FF;"></div>
-                <div class="ams-tab-dot" style="background: #FF0000;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 2</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #fff; border-color: #ccc;"></div>
-                <div class="ams-tab-dot" style="background: #333;"></div>
-                <div class="ams-tab-dot" style="background: #666;"></div>
-                <div class="ams-tab-dot" style="background: #999;"></div>
-              </div>
-              <span class="ams-tab-label">AMS 3</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #8844AA;"></div>
-              </div>
-              <span class="ams-tab-label">HT 1</span>
-            </div>
-            <div class="ams-tab">
-              <div class="ams-tab-dots">
-                <div class="ams-tab-dot" style="background: #44AA88;"></div>
-              </div>
-              <span class="ams-tab-label">HT 2</span>
-            </div>
-          </div>
-
-          <!-- AMS Content Panel -->
-          <div class="ams-content">
-            <div class="ams-info-row">
-              <div class="ams-humidity"><img src="icons/water.svg"> 18 %</div>
-              <div class="ams-humidity"><span class="sun">&#9788;</span></div>
-            </div>
-
-            <div class="ams-main">
-              <div class="ams-slots-container">
-                <div class="slot-labels-row">
-                  <div class="slot-label-item"><div class="slot-label-badge">A1&#8595;</div></div>
-                  <div class="slot-label-item"><div class="slot-label-badge">A2&#8595;</div></div>
-                  <div class="slot-label-item"><div class="slot-label-badge">A3&#8595;</div></div>
-                  <div class="slot-label-item"><div class="slot-label-badge">A4&#8595;</div></div>
-                </div>
-
-                <div class="ams-slots">
-                  <div class="ams-slot active">
-                    <div class="ams-slot-fill" style="background: #FFD700;">
-                      <span class="ams-slot-type dark">PLA</span>
-                      <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #1a1a1a;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #8B4513;">
-                      <span class="ams-slot-type">PETG</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                  <div class="ams-slot">
-                    <div class="ams-slot-fill" style="background: #222;">
-                      <span class="ams-slot-type">PLA</span>
-                      <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                    </div>
-                  </div>
-                </div>
-
-                <div class="ams-connectors">
-                  <div class="connector-lines">
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-line"><div class="vline"></div></div>
-                    <div class="connector-hbar"></div>
-                  </div>
-                </div>
-
-                <div class="hub-row">
-                  <div class="hub-line-left"></div>
-                  <div class="hub-graphic">
-                    <div class="hub-port"></div>
-                    <div class="hub-port"></div>
-                  </div>
-                  <div class="hub-line-right"></div>
-                </div>
-              </div>
-
-              <div class="ext-spool-section">
-                <div class="ext-label">Ext</div>
-                <div class="ext-row">
-                  <div>
-                    <div class="ext-slot">
-                      <span class="question">?</span>
-                      <span class="angle">&#8736;</span>
-                    </div>
-                    <div class="ext-connector"></div>
-                  </div>
-                  <div class="spool-holder">
-                    <img src="icons/single-extruder1.png">
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <div class="ams-actions">
-            <button class="auto-refill-btn">Auto-refill</button>
-            <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-            <div class="ams-spacer"></div>
-            <button class="ams-action-btn">Unload</button>
-            <button class="ams-action-btn">Load</button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

+ 0 - 924
mockup/control-page-v9.html

@@ -1,924 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>BambuTrack Control - v9</title>
-  <style>
-    * { margin: 0; padding: 0; box-sizing: border-box; }
-
-    :root {
-      --bg-main: #f5f5f5;
-      --bg-white: #ffffff;
-      --bg-light: #fafafa;
-      --bg-panel: #f5f5f5;
-      --bg-hover: #e8e8e8;
-      --text-primary: #333333;
-      --text-secondary: #666666;
-      --text-muted: #999999;
-      --border: #e0e0e0;
-      --border-light: #eeeeee;
-      --accent: #00ae42;
-    }
-
-    body {
-      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-      background: var(--bg-main);
-      color: var(--text-primary);
-      font-size: 13px;
-    }
-
-    .main-layout {
-      display: grid;
-      grid-template-columns: 1fr 380px;
-      height: 100vh;
-    }
-
-    /* Left Panel - Camera */
-    .left-panel {
-      display: flex;
-      flex-direction: column;
-      background: var(--bg-main);
-    }
-
-    .camera-header {
-      display: flex;
-      align-items: center;
-      padding: 8px 12px;
-      background: var(--bg-white);
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .camera-title {
-      font-size: 13px;
-      color: var(--text-primary);
-      margin-right: auto;
-    }
-
-    .icon-btn {
-      width: 28px;
-      height: 28px;
-      border: none;
-      background: none;
-      cursor: pointer;
-      border-radius: 4px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .icon-btn:hover { background: var(--bg-hover); }
-    .icon-btn img { width: 18px; height: 18px; opacity: 0.6; }
-
-    .camera-feed {
-      flex: 1;
-      background: #1a1a1a;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #555;
-      font-size: 14px;
-    }
-
-    /* Status bar */
-    .status-bar {
-      background: var(--accent);
-      padding: 4px 12px;
-    }
-
-    /* Print Progress */
-    .print-progress {
-      background: var(--bg-white);
-      padding: 16px 20px;
-    }
-
-    .progress-label { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
-    .progress-row { display: flex; gap: 14px; align-items: center; }
-    .progress-thumb {
-      width: 60px; height: 60px;
-      background: var(--bg-light);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      display: flex; align-items: center; justify-content: center;
-      flex-direction: column;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-    .progress-thumb-icon { font-size: 20px; margin-bottom: 2px; }
-    .progress-info { flex: 1; }
-    .progress-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
-    .progress-status { color: var(--accent); font-size: 13px; margin-bottom: 8px; }
-    .progress-bar-bg { height: 4px; background: var(--border-light); border-radius: 2px; margin-bottom: 8px; }
-    .progress-bar-fill { height: 100%; background: var(--accent); border-radius: 2px; width: 0%; }
-    .progress-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
-    .progress-btns { display: flex; gap: 6px; }
-    .progress-btn {
-      width: 32px; height: 32px;
-      border: 1px solid var(--border);
-      background: var(--bg-white);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 14px;
-      color: var(--text-muted);
-    }
-    .progress-btn:hover { background: var(--bg-hover); }
-
-    /* Right Panel - Control */
-    .right-panel {
-      background: var(--bg-white);
-      border-left: 1px solid var(--border);
-      overflow-y: auto;
-      display: flex;
-      flex-direction: column;
-    }
-
-    .control-header {
-      display: flex;
-      align-items: center;
-      padding: 10px 14px;
-      border-bottom: 1px solid var(--border);
-      gap: 8px;
-    }
-
-    .control-title { font-size: 13px; margin-right: auto; color: var(--text-secondary); }
-    .header-btn {
-      padding: 6px 14px;
-      font-size: 12px;
-      border: none;
-      border-radius: 6px;
-      cursor: pointer;
-      background: var(--accent);
-      color: white;
-    }
-    .header-btn:hover { background: #009938; }
-
-    .control-body { padding: 14px; flex: 1; overflow-y: auto; }
-
-    /* Top Section: Temp + Movement side by side */
-    .top-section {
-      display: flex;
-      gap: 20px;
-      margin-bottom: 16px;
-    }
-
-    /* Temperature Column */
-    .temp-column { flex: 0 0 auto; }
-
-    .temp-row {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 4px 0;
-    }
-
-    .temp-icon { width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; }
-    .temp-icon img { width: 16px; opacity: 0.5; }
-
-    .temp-badge {
-      font-size: 10px;
-      font-weight: 600;
-      color: var(--accent);
-      background: #e8f5e9;
-      padding: 2px 6px;
-      border-radius: 4px;
-      min-width: 16px;
-      text-align: center;
-    }
-
-    .temp-value { font-size: 16px; font-weight: 500; }
-    .temp-target { font-size: 14px; color: var(--text-muted); }
-
-    /* Air Condition */
-    .air-section {
-      display: flex;
-      align-items: center;
-      gap: 10px;
-      padding: 10px 0;
-      margin-top: 8px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .air-label {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .air-label img { width: 16px; opacity: 0.5; }
-
-    .air-item {
-      display: flex;
-      align-items: center;
-      gap: 5px;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .air-item img { width: 16px; opacity: 0.5; }
-    .lamp-label { display: flex; align-items: center; gap: 5px; }
-    .lamp-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); }
-
-    /* Movement Column */
-    .movement-column {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-    }
-
-    .nozzle-toggle {
-      display: flex;
-      border-radius: 6px;
-      overflow: hidden;
-      border: 1px solid var(--border);
-      margin-bottom: 10px;
-    }
-
-    .nozzle-toggle button {
-      padding: 6px 18px;
-      border: none;
-      background: var(--bg-white);
-      font-size: 12px;
-      cursor: pointer;
-      color: var(--text-secondary);
-    }
-
-    .nozzle-toggle button:first-child { border-right: 1px solid var(--border); }
-    .nozzle-toggle button.active { background: var(--accent); color: white; }
-
-    .movement-row {
-      display: flex;
-      gap: 16px;
-      align-items: flex-start;
-    }
-
-    .jog-section { display: flex; flex-direction: column; align-items: center; }
-
-    .jog-pad {
-      position: relative;
-      width: 100px;
-      height: 100px;
-      margin-bottom: 8px;
-    }
-
-    .jog-ring {
-      width: 100%;
-      height: 100%;
-      border-radius: 50%;
-      background: linear-gradient(135deg, #f8f8f8 0%, #ebebeb 100%);
-      border: 1px solid var(--border);
-      position: relative;
-    }
-
-    .jog-label {
-      position: absolute;
-      font-size: 10px;
-      color: var(--text-muted);
-    }
-
-    .jog-label.top { top: 6px; left: 50%; transform: translateX(-50%); }
-    .jog-label.bottom { bottom: 6px; left: 50%; transform: translateX(-50%); }
-    .jog-label.left { left: 6px; top: 50%; transform: translateY(-50%); }
-    .jog-label.right { right: 6px; top: 50%; transform: translateY(-50%); }
-
-    .jog-btn {
-      position: absolute;
-      width: 20px;
-      height: 20px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-      cursor: pointer;
-      font-size: 9px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-btn:hover { background: var(--bg-hover); }
-    .jog-btn.up { top: 16px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.down { bottom: 16px; left: 50%; transform: translateX(-50%); }
-    .jog-btn.left { left: 16px; top: 50%; transform: translateY(-50%); }
-    .jog-btn.right { right: 16px; top: 50%; transform: translateY(-50%); }
-
-    .jog-home {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      width: 32px;
-      height: 32px;
-      border-radius: 50%;
-      background: var(--accent);
-      border: none;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .jog-home:hover { background: #009938; }
-    .jog-home img { width: 16px; filter: brightness(0) invert(1); }
-
-    /* Bed Controls */
-    .bed-controls {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .bed-btn {
-      padding: 6px 10px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 11px;
-      color: var(--text-secondary);
-    }
-
-    .bed-btn:hover { background: var(--bg-hover); }
-    .bed-label { font-size: 11px; color: var(--text-muted); padding: 0 4px; }
-
-    /* Extruder Section */
-    .extruder-section {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      gap: 4px;
-    }
-
-    .extruder-btn {
-      width: 28px;
-      height: 28px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 6px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-muted);
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .extruder-btn:hover { background: var(--bg-hover); }
-
-    .extruder-graphic {
-      height: 60px;
-      margin: 4px 0;
-    }
-
-    .extruder-graphic img { height: 100%; }
-    .extruder-label { font-size: 11px; color: var(--text-muted); }
-
-    /* ========== FILAMENT BOXES ========== */
-    .filament-boxes {
-      display: flex;
-      gap: 8px;
-      margin-bottom: 12px;
-      padding-bottom: 12px;
-      border-bottom: 1px solid var(--border-light);
-    }
-
-    .filament-box {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      padding: 8px 10px;
-      background: var(--bg-white);
-      border: 2px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-    }
-
-    .filament-box:hover { border-color: #ccc; }
-    .filament-box.active { border-color: var(--accent); }
-
-    .filament-dots { display: flex; gap: 3px; }
-
-    .f-dot {
-      width: 16px;
-      height: 16px;
-      border-radius: 4px;
-      border: 1px solid rgba(0,0,0,0.1);
-    }
-
-    .f-dot.empty {
-      background: #e8e8e8 !important;
-      border: 1px solid #ccc;
-    }
-
-    .f-dot-sep {
-      width: 1px;
-      height: 16px;
-      background: #ccc;
-      margin: 0 2px;
-    }
-
-    .filament-count {
-      font-size: 12px;
-      color: var(--text-muted);
-      margin-left: 4px;
-    }
-
-    /* ========== AMS SECTION ========== */
-    .ams-section {
-      display: flex;
-      gap: 12px;
-    }
-
-    .ams-unit {
-      flex: 1;
-      background: var(--bg-panel);
-      border-radius: 12px;
-      padding: 12px;
-    }
-
-    /* AMS Header */
-    .ams-header {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-bottom: 10px;
-    }
-
-    .ams-humidity {
-      display: flex;
-      align-items: center;
-      gap: 4px;
-      font-size: 13px;
-      color: var(--text-secondary);
-    }
-
-    .ams-humidity img { width: 14px; opacity: 0.5; }
-    .ams-humidity .sun { font-size: 14px; color: #f5a623; }
-
-    /* Slot Labels */
-    .slot-labels {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 6px;
-    }
-
-    .slot-label {
-      flex: 1;
-      text-align: center;
-      font-size: 11px;
-      color: var(--text-muted);
-      padding: 2px 0;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 10px;
-    }
-
-    /* AMS Slots */
-    .ams-slots {
-      display: flex;
-      gap: 6px;
-      margin-bottom: 8px;
-    }
-
-    .ams-slot {
-      flex: 1;
-      height: 70px;
-      border-radius: 8px;
-      overflow: hidden;
-      cursor: pointer;
-      border: 2px solid var(--border);
-      background: var(--bg-white);
-    }
-
-    .ams-slot:hover { border-color: #bbb; }
-    .ams-slot.active { border-color: #d4a84b; }
-
-    .ams-slot-fill {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: flex-end;
-      padding-bottom: 6px;
-    }
-
-    .ams-slot-type {
-      font-size: 11px;
-      font-weight: 600;
-      color: white;
-      text-shadow: 0 1px 2px rgba(0,0,0,0.3);
-      margin-bottom: 2px;
-    }
-
-    .ams-slot-type.dark { color: #333; text-shadow: none; }
-
-    .ams-slot-eye { width: 16px; height: 16px; }
-    .ams-slot-eye img { width: 14px; height: 14px; opacity: 0.8; }
-    .ams-slot-eye.invert img { filter: invert(1); }
-
-    .ams-slot-empty {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      color: var(--text-muted);
-      font-size: 11px;
-    }
-
-    /* Connector Lines */
-    .ams-connectors {
-      display: flex;
-      justify-content: center;
-      margin-bottom: 4px;
-    }
-
-    .connector-group {
-      display: flex;
-      gap: 6px;
-      position: relative;
-      padding: 0 10px;
-    }
-
-    .connector-line {
-      flex: 1;
-      display: flex;
-      justify-content: center;
-    }
-
-    .connector-line .vline {
-      width: 2px;
-      height: 12px;
-      background: #c0c0c0;
-    }
-
-    .connector-hbar {
-      position: absolute;
-      bottom: 0;
-      left: 20px;
-      right: 20px;
-      height: 2px;
-      background: #c0c0c0;
-    }
-
-    /* AMS Actions */
-    .ams-actions {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-top: 12px;
-      padding-top: 12px;
-      border-top: 1px solid var(--border-light);
-    }
-
-    .auto-refill-btn {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-      padding: 8px 14px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      font-size: 12px;
-      color: var(--text-secondary);
-    }
-
-    .auto-refill-btn:hover { background: var(--bg-hover); }
-
-    .ams-settings-btn {
-      width: 32px;
-      height: 32px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 8px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .ams-settings-btn:hover { background: var(--bg-hover); }
-    .ams-settings-btn img { width: 18px; opacity: 0.5; }
-
-    /* Hub */
-    .hub-section {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      gap: 4px;
-    }
-
-    .hub-line { width: 30px; height: 2px; background: #c0c0c0; }
-
-    .hub-graphic {
-      display: flex;
-      padding: 4px 6px;
-      background: var(--bg-white);
-      border: 1px solid var(--border);
-      border-radius: 4px;
-    }
-
-    .hub-port {
-      width: 8px;
-      height: 12px;
-      background: var(--accent);
-      border-radius: 2px;
-      margin: 0 2px;
-    }
-
-    .ams-spacer { flex: 1; }
-
-    .ams-action-btn {
-      padding: 8px 20px;
-      border-radius: 8px;
-      font-size: 12px;
-      cursor: pointer;
-      border: none;
-      background: #e8e8e8;
-      color: var(--text-secondary);
-    }
-
-    .ams-action-btn:hover { background: #ddd; }
-  </style>
-</head>
-<body>
-  <div class="main-layout">
-    <!-- Left Panel - Camera -->
-    <div class="left-panel">
-      <div class="camera-header">
-        <span class="camera-title">Camera</span>
-        <button class="icon-btn"><img src="icons/video-camera.svg"></button>
-        <button class="icon-btn"><img src="icons/webcam.svg"></button>
-        <button class="icon-btn"><img src="icons/settings.svg"></button>
-        <button class="icon-btn"><img src="icons/reload.svg"></button>
-      </div>
-      <div class="camera-feed">Camera Feed</div>
-      <div class="status-bar"></div>
-      <div class="print-progress">
-        <div class="progress-label">Printing Progress</div>
-        <div class="progress-row">
-          <div class="progress-thumb">
-            <span class="progress-thumb-icon">&#128506;</span>
-            Bambu<br>Lab
-          </div>
-          <div class="progress-info">
-            <div class="progress-name">N/A</div>
-            <div class="progress-status">N/A</div>
-            <div class="progress-bar-bg"><div class="progress-bar-fill"></div></div>
-            <div class="progress-meta">Layer: N/A &nbsp;&nbsp; N/A</div>
-            <div class="progress-meta">Estimated finish time: N/A</div>
-          </div>
-          <div class="progress-btns">
-            <button class="progress-btn">&#8962;</button>
-            <button class="progress-btn">&#9208;</button>
-            <button class="progress-btn">&#9632;</button>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- Right Panel - Control -->
-    <div class="right-panel">
-      <div class="control-header">
-        <span class="control-title">Control</span>
-        <button class="header-btn">Printer Parts</button>
-        <button class="header-btn">Print Options</button>
-        <button class="header-btn">Calibration</button>
-      </div>
-
-      <div class="control-body">
-        <!-- Top Section: Temp + Movement side by side -->
-        <div class="top-section">
-          <!-- Temperature Column -->
-          <div class="temp-column">
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">L</span>
-              <span class="temp-value">24</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/hotend.svg"></div>
-              <span class="temp-badge">R</span>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/heatbed.svg"></div>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-            <div class="temp-row">
-              <div class="temp-icon"><img src="icons/chamber.svg"></div>
-              <span class="temp-value">23</span>
-              <span class="temp-target">/0 °C</span>
-            </div>
-
-            <!-- Air Condition -->
-            <div class="air-section">
-              <div class="air-label"><img src="icons/ventilation.svg"> Air Condition</div>
-            </div>
-            <div style="display: flex; align-items: center; gap: 12px; margin-top: 6px;">
-              <div class="air-item"><img src="icons/ventilation.svg"> 100%</div>
-              <div class="lamp-label">Lamp <div class="lamp-dot"></div></div>
-            </div>
-          </div>
-
-          <!-- Movement Column -->
-          <div class="movement-column">
-            <div class="nozzle-toggle">
-              <button class="active">Left</button>
-              <button>Right</button>
-            </div>
-
-            <div class="movement-row">
-              <div class="jog-section">
-                <div class="jog-pad">
-                  <div class="jog-ring">
-                    <span class="jog-label top">Y</span>
-                    <span class="jog-label bottom">-Y</span>
-                    <span class="jog-label left">-X</span>
-                    <span class="jog-label right">X</span>
-                    <button class="jog-btn up">&#9650;</button>
-                    <button class="jog-btn down">&#9660;</button>
-                    <button class="jog-btn left">&#9664;</button>
-                    <button class="jog-btn right">&#9654;</button>
-                    <button class="jog-home"><img src="icons/home.svg"></button>
-                  </div>
-                </div>
-                <div class="bed-controls">
-                  <button class="bed-btn">&#8593;10</button>
-                  <button class="bed-btn">&#8593;1</button>
-                  <span class="bed-label">Bed</span>
-                  <button class="bed-btn">&#8595;1</button>
-                  <button class="bed-btn">&#8595;10</button>
-                </div>
-              </div>
-
-              <div class="extruder-section">
-                <button class="extruder-btn">&#9650;</button>
-                <div class="extruder-graphic"><img src="icons/dual-extruder.png"></div>
-                <button class="extruder-btn">&#9660;</button>
-                <span class="extruder-label">Extruder</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Filament Assignment Boxes -->
-        <div class="filament-boxes">
-          <div class="filament-box active">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #FFD700;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-            </div>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot" style="background: #333;"></div>
-              <div class="f-dot-sep"></div>
-              <div class="f-dot" style="background: #FF3366;"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-          <div class="filament-box">
-            <div class="filament-dots">
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-              <div class="f-dot empty"></div>
-            </div>
-            <span class="filament-count">0</span>
-          </div>
-        </div>
-
-        <!-- AMS Section - Two units side by side -->
-        <div class="ams-section">
-          <!-- AMS Unit 1 -->
-          <div class="ams-unit">
-            <div class="ams-header">
-              <div class="ams-humidity"><img src="icons/water.svg"> 17 %</div>
-              <div class="ams-humidity"><span class="sun">&#9728;</span></div>
-            </div>
-            <div class="slot-labels">
-              <div class="slot-label">A1&#8595;</div>
-              <div class="slot-label">A2&#8595;</div>
-              <div class="slot-label">A3&#8595;</div>
-              <div class="slot-label">A4&#8595;</div>
-            </div>
-            <div class="ams-slots">
-              <div class="ams-slot active">
-                <div class="ams-slot-fill" style="background: #FFD700;">
-                  <span class="ams-slot-type dark">PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #333;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #8B4513;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-
-          <!-- AMS Unit 2 -->
-          <div class="ams-unit">
-            <div class="ams-header">
-              <div class="ams-humidity"><img src="icons/water.svg"> 14 %</div>
-              <div class="ams-humidity"><span class="sun">&#9728;</span></div>
-            </div>
-            <div class="slot-labels">
-              <div class="slot-label">B1&#8595;</div>
-              <div class="slot-label">B2&#8595;</div>
-              <div class="slot-label">B3&#8595;</div>
-              <div class="slot-label">B4&#8595;</div>
-            </div>
-            <div class="ams-slots">
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #888;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #666;">
-                  <span class="ams-slot-type">PETG</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #888;">
-                  <span class="ams-slot-type">PLA</span>
-                  <div class="ams-slot-eye invert"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-              <div class="ams-slot">
-                <div class="ams-slot-fill" style="background: #aaa;">
-                  <span class="ams-slot-type dark">Sup.PLA</span>
-                  <div class="ams-slot-eye"><img src="icons/eye.svg"></div>
-                </div>
-              </div>
-            </div>
-            <div class="ams-connectors">
-              <div class="connector-group">
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-line"><div class="vline"></div></div>
-                <div class="connector-hbar"></div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- AMS Actions -->
-        <div class="ams-actions">
-          <button class="auto-refill-btn">Auto-refill</button>
-          <button class="ams-settings-btn"><img src="icons/ams-settings.svg"></button>
-          <div class="hub-section">
-            <div class="hub-line"></div>
-            <div class="hub-graphic">
-              <div class="hub-port"></div>
-              <div class="hub-port"></div>
-            </div>
-            <div class="hub-line"></div>
-          </div>
-          <div class="ams-spacer"></div>
-          <button class="ams-action-btn">Unload</button>
-          <button class="ams-action-btn">Load</button>
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-BpSfhfce.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-CVJMI5JU.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DRrbGWbR.js


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DbzgStXX.js


+ 2 - 2
static/index.html

@@ -7,8 +7,8 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png" />
     <link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png" />
     <link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png" />
-    <script type="module" crossorigin src="/assets/index-DbzgStXX.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-BpSfhfce.css">
+    <script type="module" crossorigin src="/assets/index-DRrbGWbR.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-CVJMI5JU.css">
   </head>
   <body>
     <div id="root"></div>

Some files were not shown because too many files changed in this diff