laptop_specs / index.html
bitsnaps's picture
Upload 2 files
5a6e183 verified
Raw History Blame Contribute Delete
43.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Laptop Price Engine</title>
<style>
:root {
--obsidian:#0A0D14;--radar:#161F30;--radar-light:#1C2740;
--cyan:#00F5FF;--cyan-dim:rgba(0,245,255,0.15);--cyan-glow:0 0 12px rgba(0,245,255,0.35),0 0 30px rgba(0,245,255,0.08);
--mint:#10E79D;--mint-dim:rgba(16,231,157,0.12);--mint-glow:0 0 10px rgba(16,231,157,0.3);
--amber:#FFB020;--amber-dim:rgba(255,176,32,0.12);--red:#FF4757;
--white:#E6EDF3;--white-dim:#8B949E;
--border:rgba(0,245,255,0.08);--border-focus:rgba(0,245,255,0.4);
--mono:'JetBrains Mono','Fira Code','SF Mono','Cascadia Code','Consolas',monospace;
--sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
--radius:6px;--transition:180ms cubic-bezier(0.4,0,0.2,1);
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{font-size:14px;-webkit-font-smoothing:antialiased}
body{font-family:var(--sans);background:var(--obsidian);color:var(--white);min-height:100vh;overflow-x:hidden}
body::before{content:'';position:fixed;inset:0;background-image:linear-gradient(rgba(0,245,255,0.02) 1px,transparent 1px),linear-gradient(90deg,rgba(0,245,255,0.02) 1px,transparent 1px);background-size:40px 40px;pointer-events:none;z-index:0}
.topbar{position:sticky;top:0;z-index:100;display:flex;align-items:center;justify-content:space-between;padding:10px 24px;background:rgba(10,13,20,0.92);backdrop-filter:blur(12px);border-bottom:1px solid var(--border)}
.topbar-left{display:flex;align-items:center;gap:16px}
.logo{font-family:var(--mono);font-size:0.8rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--cyan);text-shadow:var(--cyan-glow)}
.logo span{color:var(--white-dim);font-weight:400}
.status-dot{width:7px;height:7px;border-radius:50%;background:var(--mint);box-shadow:var(--mint-glow);animation:pulse-dot 2s ease-in-out infinite}
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:0.4}}
.badge{font-family:var(--mono);font-size:0.65rem;padding:3px 8px;border-radius:3px;background:var(--cyan-dim);color:var(--cyan);border:1px solid rgba(0,245,255,0.12);letter-spacing:0.5px}
.badge.mint{background:var(--mint-dim);color:var(--mint);border-color:rgba(16,231,157,0.12)}
.badge.amber{background:var(--amber-dim);color:var(--amber);border-color:rgba(255,176,32,0.12)}
.main-grid{position:relative;z-index:1;display:grid;grid-template-columns:1fr 380px;gap:20px;max-width:1280px;margin:20px auto;padding:0 24px}
@media(max-width:900px){.main-grid{grid-template-columns:1fr}}
.panel{background:var(--radar);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}
.panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);background:rgba(0,0,0,0.2)}
.panel-title{font-family:var(--mono);font-size:0.7rem;font-weight:600;letter-spacing:2px;text-transform:uppercase;color:var(--white-dim)}
.panel-title .accent{color:var(--cyan)}
.panel-body{padding:16px}
.spec-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media(max-width:600px){.spec-grid{grid-template-columns:1fr}}
.field{display:flex;flex-direction:column;gap:5px}
.field-label{font-family:var(--mono);font-size:0.65rem;letter-spacing:1.5px;text-transform:uppercase;color:var(--white-dim);display:flex;align-items:center;gap:6px}
.field-label .weight{font-size:0.55rem;color:var(--amber);opacity:0.7}
select,input[type="number"]{font-family:var(--mono);font-size:0.85rem;padding:8px 10px;background:rgba(0,0,0,0.3);color:var(--white);border:1px solid var(--border);border-radius:4px;outline:none;transition:var(--transition);width:100%;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%238B949E'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}
select option{background:var(--radar);color:var(--white)}
select:hover,input[type="number"]:hover{border-color:rgba(0,245,255,0.2)}
select:focus,input[type="number"]:focus{border-color:var(--cyan);box-shadow:var(--cyan-glow)}
.field.glow select:focus,.field.glow input:focus{box-shadow:0 0 16px rgba(0,245,255,0.25),0 0 40px rgba(0,245,255,0.06)}
select:disabled,input[type="number"]:disabled{opacity:0.25;cursor:not-allowed;border-color:transparent!important;box-shadow:none!important;background:rgba(0,0,0,0.15)}
select:disabled option{color:var(--white-dim)}
.field.na{position:relative}
.field.na::after{content:'N/A';position:absolute;right:30px;top:50%;transform:translateY(-50%);font-family:var(--mono);font-size:0.7rem;color:var(--amber);opacity:0.7;pointer-events:none}
.toggle.disabled{opacity:0.2;pointer-events:none;cursor:not-allowed}
.constraint-hint{font-family:var(--mono);font-size:0.6rem;color:var(--amber);opacity:0;margin-top:2px;letter-spacing:0.5px;min-height:14px;transition:opacity 200ms}
.contrib-hint{font-family:var(--mono);font-size:0.58rem;letter-spacing:0.3px;min-height:15px;line-height:15px;opacity:0.75;transition:opacity 150ms,color 150ms}
.contrib-hint.up{color:var(--amber)}
.contrib-hint.down{color:var(--mint)}
.contrib-hint.flat{color:var(--white-dim);opacity:0.3}
.slider-row{display:flex;align-items:center;gap:10px}
.slider-row input[type="range"]{flex:1;-webkit-appearance:none;height:4px;background:var(--radar-light);border-radius:2px;outline:none}
.slider-row input[type="range"]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:var(--cyan);box-shadow:var(--cyan-glow);cursor:pointer;transition:var(--transition)}
.slider-row input[type="range"]::-webkit-slider-thumb:hover{transform:scale(1.2)}
.slider-value{font-family:var(--mono);font-size:0.8rem;color:var(--cyan);min-width:50px;text-align:right;text-shadow:0 0 8px rgba(0,245,255,0.3)}
.toggle-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px}
.toggle{display:flex;align-items:center;gap:8px;padding:6px 10px;background:rgba(0,0,0,0.2);border:1px solid var(--border);border-radius:4px;cursor:pointer;transition:var(--transition);user-select:none}
.toggle:hover{border-color:rgba(0,245,255,0.2)}
.toggle.active{border-color:rgba(0,245,255,0.3);background:var(--cyan-dim)}
.toggle-switch{width:28px;height:14px;border-radius:7px;background:var(--radar-light);position:relative;transition:var(--transition);flex-shrink:0}
.toggle-switch::after{content:'';position:absolute;top:2px;left:2px;width:10px;height:10px;border-radius:50%;background:var(--white-dim);transition:var(--transition)}
.toggle.active .toggle-switch{background:rgba(0,245,255,0.3)}
.toggle.active .toggle-switch::after{left:16px;background:var(--cyan);box-shadow:0 0 6px rgba(0,245,255,0.5)}
.toggle-label{font-family:var(--mono);font-size:0.68rem;color:var(--white-dim);letter-spacing:0.5px}
.toggle.active .toggle-label{color:var(--white)}
.toggle .contrib-hint{width:100%;text-align:center;font-size:0.52rem;min-height:13px;line-height:13px;margin-top:1px}
.section-label{font-family:var(--mono);font-size:0.6rem;letter-spacing:2px;text-transform:uppercase;color:var(--white-dim);opacity:0.5;margin:12px 0 8px;padding-bottom:4px;border-bottom:1px dashed rgba(255,255,255,0.06)}
.right-col{display:flex;flex-direction:column;gap:20px}
.prediction-panel .panel-body{padding:24px 20px;text-align:center}
.pred-label{font-family:var(--mono);font-size:0.65rem;letter-spacing:2px;text-transform:uppercase;color:var(--white-dim);margin-bottom:8px}
.pred-value{font-family:var(--mono);font-size:2.2rem;font-weight:700;color:var(--cyan);text-shadow:0 0 20px rgba(0,245,255,0.4),0 0 60px rgba(0,245,255,0.1);letter-spacing:1px;transition:all 300ms ease;line-height:1.2}
.pred-value.loading{opacity:0.4;transform:scale(0.98)}
.pred-sub{font-family:var(--mono);font-size:0.7rem;color:var(--white-dim);margin-top:4px}
.confidence-section{margin-top:20px}
.confidence-canvas-wrap{position:relative;width:100%;height:70px;margin-top:10px}
.confidence-canvas-wrap canvas{width:100%;height:100%}
.confidence-labels{display:flex;justify-content:space-between;font-family:var(--mono);font-size:0.6rem;color:var(--white-dim);margin-top:4px}
.confidence-labels .mint{color:var(--mint)}
.confidence-labels .cyan{color:var(--cyan)}
.confidence-labels .amber{color:var(--amber)}
.metrics-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:16px}
.metric-card{padding:10px 12px;background:rgba(0,0,0,0.2);border:1px solid var(--border);border-radius:4px}
.metric-label{font-family:var(--mono);font-size:0.55rem;letter-spacing:1.5px;text-transform:uppercase;color:var(--white-dim);opacity:0.7}
.metric-value{font-family:var(--mono);font-size:1.1rem;font-weight:600;color:var(--mint);margin-top:2px}
.metric-value.cyan{color:var(--cyan)}
.metric-value.amber{color:var(--amber)}
.importance-list{display:flex;flex-direction:column;gap:6px}
.importance-row{display:flex;align-items:center;gap:8px}
.importance-name{font-family:var(--mono);font-size:0.65rem;color:var(--white-dim);min-width:90px;text-align:right}
.importance-bar-bg{flex:1;height:6px;background:rgba(0,0,0,0.3);border-radius:3px;overflow:hidden}
.importance-bar{height:100%;border-radius:3px;background:linear-gradient(90deg,var(--cyan),var(--mint));transition:width 500ms cubic-bezier(0.4,0,0.2,1)}
.importance-pct{font-family:var(--mono);font-size:0.6rem;color:var(--white-dim);min-width:32px}
.loading-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:var(--obsidian);flex-direction:column;gap:16px;transition:opacity 400ms}
.loading-overlay.hidden{opacity:0;pointer-events:none}
.loading-text{font-family:var(--mono);font-size:0.75rem;letter-spacing:2px;color:var(--cyan);animation:blink 1s steps(2) infinite}
@keyframes blink{50%{opacity:0}}
.loading-bar{width:200px;height:2px;background:var(--radar);border-radius:1px;overflow:hidden}
.loading-bar-inner{height:100%;background:var(--cyan);box-shadow:var(--cyan-glow);animation:load-slide 1s ease-in-out infinite}
@keyframes load-slide{0%{width:0;margin-left:0}50%{width:60%;margin-left:20%}100%{width:0;margin-left:100%}}
::-webkit-scrollbar{width:6px}
::-webkit-scrollbar-track{background:var(--obsidian)}
::-webkit-scrollbar-thumb{background:var(--radar-light);border-radius:3px}
::-webkit-scrollbar-thumb:hover{background:rgba(0,245,255,0.2)}
.error-toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);font-family:var(--mono);font-size:0.75rem;padding:10px 20px;background:rgba(255,71,87,0.15);border:1px solid rgba(255,71,87,0.3);border-radius:4px;color:var(--red);z-index:9000;opacity:0;transition:opacity 200ms}
.error-toast.show{opacity:1}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
</head>
<body>
<div class="loading-overlay" id="loader"><div class="loading-text" id="loading-text">LOADING ONNX RUNTIME</div><div class="loading-bar"><div class="loading-bar-inner"></div></div></div>
<div class="topbar">
<div class="topbar-left">
<div class="logo">PRICE ENGINE <span>// quant-lab</span></div>
<div class="status-dot"></div>
<span class="badge">XGBoost v4</span>
<span class="badge mint" id="badge-r2">R2 —</span>
</div>
<div style="display:flex;gap:8px;align-items:center">
<span class="badge">N=<span id="badge-n">—</span></span>
<span class="badge amber">32 FEATURES</span>
<span class="badge" style="background:rgba(0,245,255,0.06)">IN-BROWSER</span>
</div>
</div>
<div class="main-grid">
<div style="display:flex;flex-direction:column;gap:20px">
<div class="panel">
<div class="panel-header"><span class="panel-title"><span class="accent">[01]</span> Core Specification Matrix</span><span class="badge">PRIMARY</span></div>
<div class="panel-body">
<div class="spec-grid">
<div class="field glow"><label class="field-label">Brand <span class="weight">■ HIGH</span></label><select id="brand"></select></div>
<div class="field glow"><label class="field-label">Condition <span class="weight">■ HIGH</span></label><select id="condition"></select></div>
<div class="field glow"><label class="field-label">CPU Tier <span class="weight">■ CRITICAL</span></label><select id="cpu_tier"></select></div>
<div class="field glow"><label class="field-label">GPU Tier <span class="weight">■ HIGH</span></label><select id="gpu_tier"></select><div class="constraint-hint" id="gaming-hint"></div></div>
<div class="field"><label class="field-label">RAM Type</label><select id="ram_type"></select></div>
<div class="field"><label class="field-label">Resolution</label><select id="resolution"></select></div>
</div>
<div class="section-label">Capacity Parameters</div>
<div class="spec-grid">
<div class="field glow"><label class="field-label">RAM <span class="weight">■ CRITICAL</span></label><div class="slider-row"><input type="range" id="ram_gb" min="2" max="64" step="2" value="8"><span class="slider-value" id="ram_gb_val">8 GB</span></div></div>
<div class="field glow"><label class="field-label">Storage <span class="weight">■ HIGH</span></label><div class="slider-row"><input type="range" id="storage_gb" min="32" max="4096" step="8" value="256"><span class="slider-value" id="storage_gb_val">256 GB</span></div></div>
<div class="field"><label class="field-label">Storage Type</label><select id="storage_type"><option value="SSD">SSD</option><option value="HDD">HDD</option><option value="eMMC">eMMC</option><option value="Hybrid">SSD + HDD</option></select><div class="constraint-hint" id="storage-type-hint"></div></div>
<div class="field"><label class="field-label">Screen Size</label><div class="slider-row"><input type="range" id="screen_inch" min="11" max="18" step="0.1" value="15.6"><span class="slider-value" id="screen_inch_val">15.6"</span></div></div>
</div>
<div class="section-label">Advanced Telemetry</div>
<div class="spec-grid">
<div class="field"><label class="field-label">Refresh Rate (Hz)</label><input type="number" id="refresh_hz" min="30" max="500" step="1" placeholder="auto: 60"></div>
<div class="field"><label class="field-label">CPU Generation</label><input type="number" id="cpu_gen" min="1" max="20" step="1" placeholder="auto"></div>
<div class="field"><label class="field-label">CPU Cores</label><input type="number" id="cpu_cores" min="1" max="32" step="1" placeholder="auto"></div>
<div class="field"><label class="field-label">RAM Speed (MHz)</label><input type="number" id="ram_speed_mhz" min="0" max="10000" step="100" placeholder="auto"></div>
<div class="field"><label class="field-label">Warranty</label><select id="warranty_tier"><option value="0">None</option><option value="1">≤ 3 months</option><option value="2">≤ 6 months</option><option value="3">≤ 12 months</option></select></div>
<div class="field"><label class="field-label">City Price Tier</label><select id="city_price_tier"><option value="0">Low-cost Wilaya</option><option value="1" selected>Mid-range Wilaya</option><option value="2">Premium Wilaya</option></select></div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header"><span class="panel-title"><span class="accent">[02]</span> Feature Flags</span><span class="badge">BINARY</span></div>
<div class="panel-body">
<div class="toggle-grid">
<div class="toggle" data-field="is_gaming"><div class="toggle-switch"></div><span class="toggle-label">Gaming</span></div>
<div class="toggle" data-field="is_ultrabook"><div class="toggle-switch"></div><span class="toggle-label">Ultrabook</span></div>
<div class="toggle" data-field="is_workstation_pro"><div class="toggle-switch"></div><span class="toggle-label">Workstation</span></div>
<div class="toggle" data-field="is_2in1"><div class="toggle-switch"></div><span class="toggle-label">2-in-1</span></div>
<div class="toggle" data-field="is_oled"><div class="toggle-switch"></div><span class="toggle-label">OLED</span></div>
<div class="toggle active" data-field="has_windows"><div class="toggle-switch"></div><span class="toggle-label">Windows</span></div>
<div class="toggle" data-field="is_touchscreen"><div class="toggle-switch"></div><span class="toggle-label">Touchscreen</span></div>
<div class="toggle" data-field="has_backlit_kb"><div class="toggle-switch"></div><span class="toggle-label">Backlit KB</span></div>
<div class="toggle" data-field="has_fingerprint"><div class="toggle-switch"></div><span class="toggle-label">Fingerprint</span></div>
</div>
<div class="constraint-hint" id="2in1-hint"></div>
</div>
</div>
</div>
<div class="right-col">
<div class="panel prediction-panel">
<div class="panel-header"><span class="panel-title"><span class="accent">[03]</span> Predicted Market Value</span><span class="badge mint" id="badge-confidence">—</span></div>
<div class="panel-body">
<div class="pred-label">ESTIMATED FAIR PRICE</div>
<div class="pred-value" id="pred-value">— DA</div>
<div class="pred-sub" id="pred-sub">Loading model...</div>
<div class="confidence-section">
<div class="pred-label" style="margin-bottom:0">CONFIDENCE SPREAD</div>
<div class="confidence-canvas-wrap"><canvas id="confidence-canvas"></canvas></div>
<div class="confidence-labels"><span class="amber" id="conf-low">-30%</span><span class="cyan" id="conf-mid">PREDICTED</span><span class="mint" id="conf-high">+30%</span></div>
</div>
<div class="metrics-grid">
<div class="metric-card"><div class="metric-label">Within +/-10%</div><div class="metric-value cyan" id="m-within10">—</div></div>
<div class="metric-card"><div class="metric-label">Within +/-20%</div><div class="metric-value" id="m-within20">—</div></div>
<div class="metric-card"><div class="metric-label">Within +/-30%</div><div class="metric-value" id="m-within30">—</div></div>
<div class="metric-card"><div class="metric-label">MAPE</div><div class="metric-value amber" id="m-mape">—</div></div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header"><span class="panel-title"><span class="accent">[04]</span> Feature Weight Analysis</span></div>
<div class="panel-body"><div class="importance-list" id="importance-list"></div></div>
</div>
</div>
</div>
<div class="error-toast" id="error-toast"></div>
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.0/dist/ort.min.js"></script>
<script>
const ARTEFACTS = {"feature_cols":["ram_gb","storage_gb","is_ssd","is_hdd","is_emmc","screen_inch","cpu_gen","refresh_hz","cpu_tier_enc","gpu_tier_enc","brand_enc","condition_enc","resolution_enc","ram_type_enc","storage_tier_enc","ram_tier_enc","is_touchscreen","has_backlit_kb","has_fingerprint","has_dedicated_gpu","is_gaming","is_ultrabook","is_workstation_pro","is_2in1","is_oled","is_nvme","warranty_tier","ram_speed_mhz","cpu_cores","has_windows","is_apple","city_price_tier"],"encoders":{"cpu_tier":{"AMD_A_Series":0,"Apple_M1":1,"Apple_M2":2,"Apple_M3":3,"Apple_M4":4,"Apple_M5":5,"Celeron":6,"Core 5":7,"Core Ultra 5":8,"Core Ultra 7":9,"Core Ultra 9":10,"Core i3":11,"Core i5":12,"Core i7":13,"Core i9":14,"Intel_N_Series":15,"Pentium":16,"Ryzen 3":17,"Ryzen 5":18,"Ryzen 7":19,"Ryzen 9":20,"Snapdragon":21,"unknown":22},"gpu_tier":{"AMD_Radeon_Dedicated":0,"Apple_GPU":1,"GTX_1050":2,"GTX_1070":3,"GTX_1650":4,"GTX_1660":5,"GTX_900":6,"GTX_Other":7,"GeForce_MX110":8,"GeForce_MX130":9,"GeForce_MX150":10,"GeForce_MX230":11,"GeForce_MX250":12,"GeForce_MX330":13,"GeForce_MX350":14,"GeForce_MX450":15,"GeForce_MX550":16,"GeForce_MX555":17,"GeForce_MX570":18,"GeForce_MX630":19,"GeForce_MX635":20,"GeForce_MX650":21,"GeForce_MX750":22,"GeForce_MX920":23,"GeForce_MX930":24,"Integrated_High":25,"Integrated_Low":26,"Integrated_Medium":27,"Intel_Arc":28,"NVIDIA_Low":29,"Quadro":30,"Qualcomm_Adreno":31,"RTX_2050":32,"RTX_2060":33,"RTX_2070":34,"RTX_2080":35,"RTX_3050":36,"RTX_3060":37,"RTX_3070":38,"RTX_3080":39,"RTX_4050":40,"RTX_4060":41,"RTX_4070":42,"RTX_4080":43,"RTX_4090":44,"RTX_5060":45,"RTX_5070":46,"RTX_5070Ti":47,"RTX_5080":48,"RTX_5090":49,"RTX_A_Series":50,"RX_5000":51,"RX_6600":52,"RX_6800":53,"RX_7600":54,"RX_Vega":55,"unknown":56},"brand":{"ACER":0,"APPLE":1,"ASUS":2,"CONDOR":3,"DELL":4,"FUJITSU":5,"GIGABYTE":6,"HONOR":7,"HP":8,"HUAWEI":9,"LENOVO":10,"LG":11,"MACBOOK":12,"MEDION":13,"MICROSOFT":14,"MSI":15,"RAZER":16,"REDMI":17,"SAMSUNG":18,"SURFACE":19,"THOMSON":20,"TOSHIBA":21,"VAIO":22,"XIAOMI":23,"unknown":24},"condition":{"like_new":0,"neuf":1,"occasion":2,"unknown":3},"resolution":{"2.8K_OLED":0,"3K":1,"4K_UHD":2,"FHD":3,"HD":4,"HD+":5,"Retina_QHD":6,"WUXGA":7,"unknown":8},"ram_type":{"DDR3":0,"DDR4":1,"DDR5":2,"LPDDR4":3,"LPDDR5":4,"Unified":5,"unknown":6},"storage_tier":{"large":0,"medium":1,"nan":2,"small":3,"tiny":4,"xl":5},"ram_tier":{"16gb":0,"32gb":1,"4gb":2,"64gb+":3,"8gb":4}},"model_name":"XGBoost_v4","r2":0.8665,"mape":17.8,"within_10":47.7,"within_20":73.9,"within_30":86.0,"n_train":14815,"n_features":32,"feature_importance":{"ram_gb":0.039227,"storage_gb":0.090552,"is_ssd":0.00675,"is_hdd":0.008264,"is_emmc":0.001447,"screen_inch":0.030316,"cpu_gen":0.015105,"refresh_hz":0.008671,"cpu_tier_enc":0.021775,"gpu_tier_enc":0.049331,"brand_enc":0.007969,"condition_enc":0.011017,"resolution_enc":0.007983,"ram_type_enc":0.010218,"storage_tier_enc":0.030056,"ram_tier_enc":0.024285,"is_touchscreen":0.007596,"has_backlit_kb":0.005459,"has_fingerprint":0.004762,"has_dedicated_gpu":0.458922,"is_gaming":0.026967,"is_ultrabook":0.004656,"is_workstation_pro":0.009713,"is_2in1":0.005052,"is_oled":0.028103,"is_nvme":0.004939,"warranty_tier":0.008385,"ram_speed_mhz":0.011537,"cpu_cores":0.012064,"has_windows":0.00564,"is_apple":0.033719,"city_price_tier":0.00952}};
let session=null,debounceTimer=null,lastPrice=null,isApplyingConstraints=false;
const toggleState={is_gaming:false,is_ultrabook:false,is_workstation_pro:false,is_2in1:false,is_oled:false,has_windows:true,is_touchscreen:false,has_backlit_kb:false,has_fingerprint:false};
const MEDIAN_CPU_GEN=12,MEDIAN_REFRESH=60,MEDIAN_RAM_SPEED=3200,MEDIAN_CPU_CORES=6;
const STORAGE_BINS=[0,128,256,512,1024,4096],STORAGE_LABELS=["tiny","small","medium","large","xl"];
const RAM_BINS=[0,4,8,16,32,128],RAM_LABELS=["4gb","8gb","16gb","32gb","64gb+"];
const CPU_GEN_DEFAULTS={Celeron:6,Pentium:6,Intel_N_Series:5,'Core i3':12,'Core i5':12,'Core i7':12,'Core i9':13,'Core 5':1,'Core Ultra 5':1,'Core Ultra 7':1,'Core Ultra 9':1,'Ryzen 3':7,'Ryzen 5':7,'Ryzen 7':7,'Ryzen 9':8,AMD_A_Series:5};
const CPU_CORES_DEFAULTS={Celeron:2,Pentium:2,Intel_N_Series:4,'Core i3':4,'Core i5':6,'Core i7':8,'Core i9':16,'Core 5':6,'Core Ultra 5':6,'Core Ultra 7':8,'Core Ultra 9':12,'Ryzen 3':4,'Ryzen 5':6,'Ryzen 7':8,'Ryzen 9':12,AMD_A_Series:2,Apple_M1:8,Apple_M2:8,Apple_M3:8,Apple_M4:10,Apple_M5:12,Snapdragon:8};
const RAM_SPEED_DEFAULTS={DDR3:1600,DDR4:3200,DDR5:4800,LPDDR4:4266,LPDDR5:6400,Unified:0};
const APPLE_CPUS=['Apple_M1','Apple_M2','Apple_M3','Apple_M4','Apple_M5'];
const INTEL_CPUS=['Celeron','Pentium','Intel_N_Series','Core i3','Core i5','Core i7','Core i9','Core 5','Core Ultra 5','Core Ultra 7','Core Ultra 9'];
const AMD_CPUS=['AMD_A_Series','Ryzen 3','Ryzen 5','Ryzen 7','Ryzen 9'];
const APPLE_GPUS=['Apple_GPU','Integrated_High'];
const INTEL_INTEGRATED=['Integrated_High','Integrated_Medium','Integrated_Low'];
const ALL_INTEGRATED=['Integrated_High','Integrated_Medium','Integrated_Low','unknown'];
const DEDICATED_NVIDIA=['GTX_1050','GTX_1070','GTX_1650','GTX_1660','GTX_900','GTX_Other','GeForce_MX110','GeForce_MX130','GeForce_MX150','GeForce_MX230','GeForce_MX250','GeForce_MX330','GeForce_MX350','GeForce_MX450','GeForce_MX550','GeForce_MX555','GeForce_MX570','GeForce_MX630','GeForce_MX635','GeForce_MX650','GeForce_MX750','GeForce_MX920','GeForce_MX930','NVIDIA_Low','RTX_2050','RTX_2060','RTX_2070','RTX_2080','RTX_3050','RTX_3060','RTX_3070','RTX_3080','RTX_4050','RTX_4060','RTX_4070','RTX_4080','RTX_4090','RTX_5060','RTX_5070','RTX_5070Ti','RTX_5080','RTX_5090','RTX_A_Series','Quadro'];
const LOW_END_GPUS=['Integrated_Low','unknown','GeForce_MX110','GeForce_MX130','NVIDIA_Low'];
const HIGH_RES=['2.8K_OLED','3K','4K_UHD','Retina_QHD'];
const OLED_RESOLUTIONS=['2.8K_OLED','Retina_QHD','4K_UHD'];
const APPLE_DEFAULTS={cpu_tier:'Apple_M4',gpu_tier:'Apple_GPU',ram_type:'Unified',resolution:'Retina_QHD'};
function safeEncode(k,v){const e=ARTEFACTS.encoders[k];if(!e)return 0;if(e[v]!==undefined)return e[v];const vl=v.toLowerCase();for(const[c,i]of Object.entries(e))if(c.toLowerCase()===vl)return i;return 0}
function tierFromBins(v,bins,labels){for(let i=0;i<bins.length-1;i++)if(bins[i]<v&&v<=bins[i+1])return labels[i];return labels[labels.length-1]}
function formatLabel(v){return v.replace(/_/g,' ').replace(/unknown/i,'Unknown').replace(/Integrated_/g,'Integrated ').replace(/GeForce_MX/g,'GeForce MX').replace(/RTX_/g,'RTX ').replace(/GTX_/g,'GTX ').replace(/RX_/g,'RX ').replace(/Apple_M/g,'Apple M').replace(/AMD_Radeon_Dedicated/g,'AMD Radeon').replace(/Intel_N_Series/g,'Intel N-Series').replace(/AMD_A_Series/g,'AMD A-Series').replace(/RTX_A_Series/g,'RTX A-Series').replace(/Qualcomm_Adreno/g,'Qualcomm Adreno').replace(/NVIDIA_Low/g,'NVIDIA Low').replace(/GTX_Other/g,'GTX Other').replace(/MACBOOK/g,'MacBook').trim()}
function populateSelect(id,items,def){const el=document.getElementById(id);[...items].sort((a,b)=>{if(a==='unknown')return 1;if(b==='unknown')return-1;return a.localeCompare(b)}).forEach(v=>{const o=document.createElement('option');o.value=v;o.textContent=formatLabel(v);if(v===def)o.selected=true;el.appendChild(o)})}
function filterSelect(id,allowed,fb){const el=document.getElementById(id),cv=el.value,s=new Set(allowed);let hc=false;Array.from(el.options).forEach(o=>{const vis=s.has(o.value);o.hidden=!vis;if(vis&&o.value===cv)hc=true});if(!hc)el.value=fb||allowed[0]}
function setSelectValue(id,v){document.getElementById(id).value=v}
function disableField(id){const el=document.getElementById(id);el.disabled=true;el.value='';el.closest('.field')?.classList.add('na')}
function enableField(id,ph){const el=document.getElementById(id);el.disabled=false;if(ph)el.placeholder=ph;el.closest('.field')?.classList.remove('na')}
function setToggle(f,v){toggleState[f]=v;const el=document.querySelector(`.toggle[data-field="${f}"]`);if(!el)return;if(v)el.classList.add('active');else el.classList.remove('active')}
function setToggleEnabled(f,en){const el=document.querySelector(`.toggle[data-field="${f}"]`);if(!el)return;if(en)el.classList.remove('disabled');else el.classList.add('disabled')}
function formatDA(v){return Math.round(v).toLocaleString()+' DA'}
function showError(m){const el=document.getElementById('error-toast');el.textContent=m;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),4000)}
function renderImportance(){const fi=ARTEFACTS.feature_importance;const sorted=Object.entries(fi).sort((a,b)=>b[1]-a[1]).slice(0,8);const c=document.getElementById('importance-list');c.innerHTML='';sorted.forEach(([f,imp])=>{const r=document.createElement('div');r.className='importance-row';r.innerHTML=`<span class="importance-name">${formatLabel(f)}</span><div class="importance-bar-bg"><div class="importance-bar" style="width:${imp*100}%"></div></div><span class="importance-pct">${(imp*100).toFixed(1)}%</span>`;c.appendChild(r)})}
function buildFeatures(){
const brand=document.getElementById('brand').value,cpu_tier=document.getElementById('cpu_tier').value;
const gpu_tier=document.getElementById('gpu_tier').value,ram_type=document.getElementById('ram_type').value;
const storage_type=document.getElementById('storage_type').value,resolution=document.getElementById('resolution').value;
const condition=document.getElementById('condition').value;
const ram_gb=parseFloat(document.getElementById('ram_gb').value),storage_gb=parseFloat(document.getElementById('storage_gb').value);
const screen_inch=parseFloat(document.getElementById('screen_inch').value);
const refresh_hz=parseFloat(document.getElementById('refresh_hz').value)||MEDIAN_REFRESH;
const cpu_gen_raw=parseInt(document.getElementById('cpu_gen').value),cpu_cores_raw=parseInt(document.getElementById('cpu_cores').value);
const ram_speed_raw=parseFloat(document.getElementById('ram_speed_mhz').value);
const warranty_tier=parseInt(document.getElementById('warranty_tier').value),city_price_tier=parseInt(document.getElementById('city_price_tier').value);
const isApple=brand==='APPLE'||brand==='MACBOOK';
const isSSD=storage_type==='SSD'||storage_type==='Hybrid'?1:0,isHDD=storage_type==='HDD'||storage_type==='Hybrid'?1:0,isEmmc=storage_type==='eMMC'?1:0,isNVMe=isSSD;
const isDedGPU=!ALL_INTEGRATED.includes(gpu_tier)?1:0;
let cpu_gen=cpu_gen_raw||CPU_GEN_DEFAULTS[cpu_tier]||MEDIAN_CPU_GEN;
if(cpu_tier.startsWith('Apple')||cpu_tier==='Snapdragon')cpu_gen=MEDIAN_CPU_GEN;
const cpu_cores=cpu_cores_raw||CPU_CORES_DEFAULTS[cpu_tier]||MEDIAN_CPU_CORES;
const ram_speed=ram_speed_raw||RAM_SPEED_DEFAULTS[ram_type]||MEDIAN_RAM_SPEED;
const storage_tier=tierFromBins(storage_gb,STORAGE_BINS,STORAGE_LABELS);
const ram_tier=tierFromBins(ram_gb,RAM_BINS,RAM_LABELS);
const fm={ram_gb,storage_gb,is_ssd:isSSD,is_hdd:isHDD,is_emmc:isEmmc,screen_inch,cpu_gen,refresh_hz,
cpu_tier_enc:safeEncode('cpu_tier',cpu_tier),gpu_tier_enc:safeEncode('gpu_tier',gpu_tier),
brand_enc:safeEncode('brand',brand.toUpperCase()),condition_enc:safeEncode('condition',condition),
resolution_enc:safeEncode('resolution',resolution),ram_type_enc:safeEncode('ram_type',ram_type),
storage_tier_enc:safeEncode('storage_tier',storage_tier),ram_tier_enc:safeEncode('ram_tier',ram_tier),
is_touchscreen:toggleState.is_touchscreen?1:0,has_backlit_kb:toggleState.has_backlit_kb?1:0,
has_fingerprint:toggleState.has_fingerprint?1:0,has_dedicated_gpu:isDedGPU,
is_gaming:toggleState.is_gaming?1:0,is_ultrabook:toggleState.is_ultrabook?1:0,
is_workstation_pro:toggleState.is_workstation_pro?1:0,is_2in1:toggleState.is_2in1?1:0,
is_oled:toggleState.is_oled?1:0,is_nvme:isNVMe,warranty_tier,ram_speed_mhz:ram_speed,
cpu_cores,has_windows:toggleState.has_windows?1:0,is_apple:isApple?1:0,city_price_tier};
return ARTEFACTS.feature_cols.map(c=>parseFloat(fm[c]));
}
function schedulePredict(){clearTimeout(debounceTimer);debounceTimer=setTimeout(predict,150)}
async function predict(){
if(!session)return;const vec=buildFeatures();const predEl=document.getElementById('pred-value');predEl.classList.add('loading');
try{const tensor=new ort.Tensor('float32',Float32Array.from(vec),[1,32]);
const results=await session.run({input:tensor});const price=results[Object.keys(results)[0]].data[0];lastPrice=price;
predEl.classList.remove('loading');predEl.textContent=formatDA(price);
document.getElementById('pred-sub').textContent=`Model: ${ARTEFACTS.model_name} · N=${ARTEFACTS.n_train.toLocaleString()} · In-Browser ONNX`;
drawConfidenceBand(price);
document.getElementById('conf-low').textContent=formatDA(price*0.7);
document.getElementById('conf-high').textContent='+'+formatDA(price*1.3);
document.getElementById('conf-mid').textContent=formatDA(price);
computeContributions(vec,price);
}catch(e){predEl.classList.remove('loading');showError('Prediction error: '+e.message)}
}
const FEATURE_UI_MAP={brand:['brand_enc','is_apple'],condition:['condition_enc'],cpu_tier:['cpu_tier_enc','cpu_gen','cpu_cores'],gpu_tier:['gpu_tier_enc','has_dedicated_gpu'],ram_type:['ram_type_enc','ram_speed_mhz'],ram_gb:['ram_gb','ram_tier_enc'],storage_gb:['storage_gb','storage_tier_enc'],storage_type:['is_ssd','is_hdd','is_emmc','is_nvme'],resolution:['resolution_enc','is_oled'],screen_inch:['screen_inch'],refresh_hz:['refresh_hz'],warranty_tier:['warranty_tier'],city_price_tier:['city_price_tier'],is_gaming:['is_gaming'],is_ultrabook:['is_ultrabook'],is_workstation_pro:['is_workstation_pro'],is_2in1:['is_2in1'],has_windows:['has_windows'],is_touchscreen:['is_touchscreen'],has_backlit_kb:['has_backlit_kb'],has_fingerprint:['has_fingerprint']};
async function computeContributions(vec,curPrice){
const bfm={ram_gb:8,storage_gb:256,is_ssd:1,is_hdd:0,is_emmc:0,screen_inch:15.6,cpu_gen:12,refresh_hz:60,
cpu_tier_enc:safeEncode('cpu_tier','Core i5'),gpu_tier_enc:safeEncode('gpu_tier','Integrated_Medium'),
brand_enc:safeEncode('brand','LENOVO'),condition_enc:safeEncode('condition','occasion'),
resolution_enc:safeEncode('resolution','FHD'),ram_type_enc:safeEncode('ram_type','DDR4'),
storage_tier_enc:safeEncode('storage_tier',tierFromBins(256,STORAGE_BINS,STORAGE_LABELS)),
ram_tier_enc:safeEncode('ram_tier',tierFromBins(8,RAM_BINS,RAM_LABELS)),
is_touchscreen:0,has_backlit_kb:0,has_fingerprint:0,has_dedicated_gpu:0,
is_gaming:0,is_ultrabook:0,is_workstation_pro:0,is_2in1:0,is_oled:0,is_nvme:1,
warranty_tier:0,ram_speed_mhz:3200,cpu_cores:6,has_windows:1,is_apple:0,city_price_tier:1};
const bVec=ARTEFACTS.feature_cols.map(c=>parseFloat(bfm[c]));
try{const bt=new ort.Tensor('float32',Float32Array.from(bVec),[1,32]);
const br=await session.run({input:bt});const bPrice=br[Object.keys(br)[0]].data[0];
const totalDelta=curPrice-bPrice;const fi=ARTEFACTS.feature_importance;
const totalFI=Object.values(fi).reduce((a,b)=>a+b,0);
const contribs={};for(const col of ARTEFACTS.feature_cols)contribs[col]=totalDelta*(fi[col]/totalFI);
renderContributions(contribs);
}catch(e){}
}
function renderContributions(contribs){
for(const[uiId,features]of Object.entries(FEATURE_UI_MAP)){
let total=0;features.forEach(f=>{total+=(contribs[f]||0)});
let el;if(uiId.startsWith('is_')||uiId.startsWith('has_'))el=document.querySelector(`.toggle[data-field="${uiId}"]`);
else{const ctrl=document.getElementById(uiId);el=ctrl?ctrl.closest('.field'):null}
if(!el)continue;let hint=el.querySelector(':scope > .contrib-hint');
if(!hint){hint=document.createElement('div');hint.className='contrib-hint';el.appendChild(hint)}
if(Math.abs(total)<500){hint.textContent='~0 DA';hint.className='contrib-hint flat'}
else if(total>0){hint.textContent='\u2197 +'+Math.round(total).toLocaleString()+' DA';hint.className='contrib-hint up'}
else{hint.textContent='\u2198 '+Math.round(total).toLocaleString()+' DA';hint.className='contrib-hint down'}
}
}
function drawConfidenceBand(price){
const canvas=document.getElementById('confidence-canvas'),dpr=window.devicePixelRatio||1,rect=canvas.parentElement.getBoundingClientRect();
canvas.width=rect.width*dpr;canvas.height=rect.height*dpr;canvas.style.width=rect.width+'px';canvas.style.height=rect.height+'px';
const ctx=canvas.getContext('2d');ctx.scale(dpr,dpr);const w=rect.width,h=rect.height;ctx.clearRect(0,0,w,h);
const grad=ctx.createLinearGradient(0,0,w,0);grad.addColorStop(0,'rgba(255,176,32,0.08)');grad.addColorStop(0.3,'rgba(0,245,255,0.06)');grad.addColorStop(0.7,'rgba(0,245,255,0.06)');grad.addColorStop(1,'rgba(16,231,157,0.08)');ctx.fillStyle=grad;ctx.fillRect(0,0,w,h);
const c=w/2;[{x:0,w:w*0.35,c:'rgba(255,176,32,0.12)'},{x:w*0.35,w:w*0.15,c:'rgba(0,245,255,0.08)'},{x:w*0.5,w:w*0.15,c:'rgba(0,245,255,0.08)'},{x:w*0.65,w:w*0.35,c:'rgba(16,231,157,0.12)'}].forEach(z=>{ctx.fillStyle=z.c;ctx.fillRect(z.x,0,z.w,h)});
ctx.strokeStyle='rgba(0,245,255,0.6)';ctx.lineWidth=1.5;ctx.setLineDash([4,4]);ctx.beginPath();ctx.moveTo(c,0);ctx.lineTo(c,h);ctx.stroke();ctx.setLineDash([]);
ctx.strokeStyle='rgba(0,245,255,0.2)';ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(w*0.35,0);ctx.lineTo(w*0.35,h);ctx.stroke();ctx.beginPath();ctx.moveTo(w*0.65,0);ctx.lineTo(w*0.65,h);ctx.stroke();
ctx.strokeStyle='rgba(255,176,32,0.15)';ctx.setLineDash([2,3]);ctx.beginPath();ctx.moveTo(w*0.2,0);ctx.lineTo(w*0.2,h);ctx.stroke();ctx.beginPath();ctx.moveTo(w*0.8,0);ctx.lineTo(w*0.8,h);ctx.stroke();ctx.setLineDash([]);
ctx.beginPath();ctx.arc(c,h/2,5,0,Math.PI*2);ctx.fillStyle='#00F5FF';ctx.fill();
ctx.beginPath();ctx.arc(c,h/2,10,0,Math.PI*2);ctx.strokeStyle='rgba(0,245,255,0.3)';ctx.lineWidth=1;ctx.stroke();
const glow=ctx.createRadialGradient(c,h/2,0,c,h/2,30);glow.addColorStop(0,'rgba(0,245,255,0.2)');glow.addColorStop(1,'rgba(0,245,255,0)');ctx.fillStyle=glow;ctx.fillRect(c-30,h/2-30,60,60);
}
function applyAllConstraints(){
if(isApplyingConstraints)return;isApplyingConstraints=true;
const brand=document.getElementById('brand').value,cpu=document.getElementById('cpu_tier').value;
const isApple=brand==='APPLE'||brand==='MACBOOK',isAppleCPU=cpu.startsWith('Apple_');
const isLowEndCPU=['Celeron','Pentium','Intel_N_Series','AMD_A_Series'].includes(cpu);
if(isApple){filterSelect('cpu_tier',[...APPLE_CPUS,'unknown'],APPLE_DEFAULTS.cpu_tier);if(!isAppleCPU)setSelectValue('cpu_tier',APPLE_DEFAULTS.cpu_tier)}
else{filterSelect('cpu_tier',[...INTEL_CPUS,...AMD_CPUS,'Snapdragon','unknown'],'Core i5');if(isAppleCPU)setSelectValue('cpu_tier','Core i5')}
const cpuNow=document.getElementById('cpu_tier').value,isACN=cpuNow.startsWith('Apple_'),isSN=cpuNow==='Snapdragon';
const isLEN=['Celeron','Pentium','Intel_N_Series','AMD_A_Series'].includes(cpuNow);
if(isACN){filterSelect('gpu_tier',APPLE_GPUS,APPLE_DEFAULTS.gpu_tier);setSelectValue('gpu_tier',APPLE_DEFAULTS.gpu_tier)}
else if(isSN)filterSelect('gpu_tier',['Qualcomm_Adreno','Integrated_Medium','Integrated_Low','unknown'],'Qualcomm_Adreno');
else if(isLEN){const bg=[...INTEL_INTEGRATED,'unknown',...DEDICATED_NVIDIA.filter(g=>g.startsWith('GeForce_MX')||g==='GTX_1050'||g==='GTX_1650'||g==='NVIDIA_Low')];filterSelect('gpu_tier',bg,'Integrated_Medium')}
else filterSelect('gpu_tier',Object.keys(ARTEFACTS.encoders.gpu_tier),'Integrated_Medium');
if(isACN){filterSelect('ram_type',['Unified'],'Unified');setSelectValue('ram_type','Unified')}
else if(isLEN)filterSelect('ram_type',['DDR3','DDR4','LPDDR4','unknown'],'DDR4');
else filterSelect('ram_type',Object.keys(ARTEFACTS.encoders.ram_type),'DDR4');
const rt=document.getElementById('ram_type').value,as=RAM_SPEED_DEFAULTS[rt];
if(as!==undefined){document.getElementById('ram_speed_mhz').value=as;disableField('ram_speed_mhz')}else enableField('ram_speed_mhz','auto');
if(isACN||isSN){disableField('cpu_gen');disableField('cpu_cores')}else{enableField('cpu_gen','auto');enableField('cpu_cores','auto')}
if(isACN){filterSelect('resolution',['Retina_QHD','unknown'],'Retina_QHD');setSelectValue('resolution','Retina_QHD')}
else if(isLEN)filterSelect('resolution',['HD','HD+','FHD','WUXGA','unknown'],document.getElementById('resolution').value);
else filterSelect('resolution',Object.keys(ARTEFACTS.encoders.resolution),'FHD');
const gpuNow=document.getElementById('gpu_tier').value;
const isLG=LOW_END_GPUS.includes(gpuNow)||(gpuNow.startsWith('Integrated_')&&gpuNow!=='Integrated_High');
if(isLG&&!isACN){const cr=document.getElementById('resolution').value;if(!HIGH_RES.includes(cr)){const a=Object.keys(ARTEFACTS.encoders.resolution).filter(r=>!HIGH_RES.includes(r));filterSelect('resolution',a,cr)}}
if(toggleState.is_oled&&!isACN){const cr=document.getElementById('resolution').value;if(!OLED_RESOLUTIONS.includes(cr))setSelectValue('resolution','2.8K_OLED')}
const st=document.getElementById('storage_type').value,sh=document.getElementById('storage-type-hint');
if(sh){if(st==='SSD')sh.textContent='NVMe assumed';else if(st==='HDD')sh.textContent='Non-NVMe';else if(st==='Hybrid')sh.textContent='NVMe SSD + HDD';else sh.textContent='';sh.style.opacity=sh.textContent?'0.7':'0'}
if(isApple){setToggle('has_windows',false);setToggleEnabled('has_windows',false)}else setToggleEnabled('has_windows',true);
if(toggleState.is_gaming){setToggle('is_ultrabook',false);setToggleEnabled('is_ultrabook',false)}else setToggleEnabled('is_ultrabook',true);
if(toggleState.is_ultrabook){setToggle('is_gaming',false);setToggleEnabled('is_gaming',false)}else setToggleEnabled('is_gaming',true);
const gh=document.getElementById('gaming-hint');if(gh){if(toggleState.is_gaming&&ALL_INTEGRATED.includes(gpuNow)){gh.textContent='Gaming usually needs a dedicated GPU';gh.style.opacity='1'}else{gh.textContent='';gh.style.opacity='0'}}
const tih=document.getElementById('2in1-hint');if(tih){if(toggleState.is_2in1&&!toggleState.is_touchscreen){tih.textContent='Most 2-in-1s have touchscreens';tih.style.opacity='1'}else{tih.textContent='';tih.style.opacity='0'}}
isApplyingConstraints=false;
}
['brand','cpu_tier','gpu_tier','ram_type','resolution','condition','storage_type','warranty_tier','city_price_tier'].forEach(id=>{document.getElementById(id).addEventListener('change',()=>{if(['brand','cpu_tier','gpu_tier','ram_type','resolution'].includes(id))applyAllConstraints();schedulePredict()})});
['refresh_hz','cpu_gen','cpu_cores','ram_speed_mhz'].forEach(id=>{document.getElementById(id).addEventListener('input',schedulePredict)});
['ram_gb','storage_gb','screen_inch'].forEach(id=>{const sl=document.getElementById(id),d=document.getElementById(id+'_val');sl.addEventListener('input',()=>{const v=parseFloat(sl.value);if(id==='ram_gb')d.textContent=v+' GB';else if(id==='storage_gb')d.textContent=v>=1000?(v/1000).toFixed(1)+' TB':v+' GB';else d.textContent=v;schedulePredict()})});
document.querySelectorAll('.toggle').forEach(el=>{el.addEventListener('click',()=>{if(el.classList.contains('disabled'))return;const f=el.dataset.field;el.classList.toggle('active');toggleState[f]=el.classList.contains('active');applyAllConstraints();schedulePredict()})});
async function init(){
const lt=document.getElementById('loading-text');
try{lt.textContent='LOADING ONNX MODEL (~2.3 MB)...';
session=await ort.InferenceSession.create('./model.onnx',{executionProviders:['wasm']});
populateSelect('brand',Object.keys(ARTEFACTS.encoders.brand),'LENOVO');
populateSelect('cpu_tier',Object.keys(ARTEFACTS.encoders.cpu_tier),'Core i5');
populateSelect('gpu_tier',Object.keys(ARTEFACTS.encoders.gpu_tier),'Integrated_Medium');
populateSelect('ram_type',Object.keys(ARTEFACTS.encoders.ram_type),'DDR4');
populateSelect('resolution',Object.keys(ARTEFACTS.encoders.resolution),'FHD');
populateSelect('condition',Object.keys(ARTEFACTS.encoders.condition),'occasion');
applyAllConstraints();
document.getElementById('badge-r2').textContent=`R2 ${ARTEFACTS.r2}`;
document.getElementById('badge-n').textContent=ARTEFACTS.n_train.toLocaleString();
document.getElementById('m-within10').textContent=ARTEFACTS.within_10+'%';
document.getElementById('m-within20').textContent=ARTEFACTS.within_20+'%';
document.getElementById('m-within30').textContent=ARTEFACTS.within_30+'%';
document.getElementById('m-mape').textContent=ARTEFACTS.mape+'%';
document.getElementById('badge-confidence').textContent=`R2 ${ARTEFACTS.r2}`;
renderImportance();
document.getElementById('loader').classList.add('hidden');
setTimeout(()=>document.getElementById('loader').remove(),500);
predict();
}catch(e){lt.textContent='FAILED: '+e.message;showError('Failed to load model: '+e.message)}
}
init();
</script>
</body>
</html>