2025-08-27 13:34:10 +08:00

426 lines
32 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Support & Downloads · Leitan GPS</title>
<meta name="description" content="Search by model or serial to access manuals, firmware/software, knowledge base articles, and videos for construction machinery GPS products." />
<meta name="theme-color" content="#0ea5e9" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='46' fill='%230ea5e9'/%3E%3Cpath d='M50 20a30 30 0 1030 30A30 30 0 0050 20zm0 10a20 20 0 11-20 20A20 20 0 0150 30z' fill='white'/%3E%3C/svg%3E" />
<!-- Tailwind v4 Play CDN -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script type="tailwind-config">
export default {
darkMode: 'class',
theme: {
extend: {
colors: {
brand: { 50:'#eff6ff',100:'#dbeafe',200:'#bfdbfe',300:'#93c5fd',400:'#60a5fa',500:'#3b82f6',600:'#2563eb',700:'#1d4ed8',800:'#1e40af',900:'#1e3a8a' }
},
boxShadow: { soft: '0 10px 30px rgba(0,0,0,0.06)' }
}
}
}
</script>
<style>
html { scroll-behavior: smooth; }
::selection { background: #bfdbfe; }
</style>
</head>
<body class="bg-white text-gray-800 antialiased dark:bg-gray-950 dark:text-gray-100">
<!-- Decorative BG -->
<div aria-hidden="true" class="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
<div class="absolute -top-32 left-1/2 h-80 w-[1100px] -translate-x-1/2 rounded-full bg-gradient-to-r from-brand-400/30 via-sky-400/20 to-cyan-400/30 blur-3xl dark:from-brand-600/30 dark:via-sky-600/20 dark:to-cyan-600/30"></div>
</div>
<!-- Header -->
<header class="sticky top-0 z-50 backdrop-blur supports-[backdrop-filter]:bg-white/60 bg-white/80 border-b border-gray-200/70 dark:bg-gray-950/70 dark:border-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-16 items-center justify-between">
<a href="/index.html" class="flex items-center gap-2">
<span class="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-brand-600 text-white shadow-soft">L</span>
<span class="font-semibold">Leitan GPS</span>
</a>
<nav class="hidden md:flex items-center gap-8 text-sm">
<a href="/support/index.html" aria-current="page" class="text-brand-700 dark:text-brand-300 font-medium">Support</a>
<a href="/products/index.html" class="hover:text-brand-600 dark:hover:text-brand-400">Catalog</a>
<span class="h-5 w-px bg-gray-300/60 dark:bg-gray-700"></span>
<a href="/employee/index.html" class="hover:text-brand-600 dark:hover:text-brand-400">Employee</a>
<span class="h-5 w-px bg-gray-300/60 dark:bg-gray-700"></span>
<div class="flex items-center gap-3" role="group" aria-label="Language">
<button type="button" class="text-xs px-2 py-1 rounded border border-gray-300/80 hover:border-brand-500 hover:text-brand-600 dark:border-gray-700 dark:hover:border-brand-500" title="Italiano (not implemented)">IT</button>
<button type="button" class="text-xs px-2 py-1 rounded border border-brand-500 text-brand-700 dark:text-brand-300" aria-pressed="true" title="English">EN</button>
</div>
</nav>
<div class="flex items-center gap-3">
<span id="empBadge" class="hidden sm:inline-flex items-center gap-1 rounded-full border border-gray-300 px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:text-gray-300"></span>
<button id="themeToggle" aria-label="Toggle theme" class="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-gray-300 bg-white shadow-sm hover:border-brand-500 dark:bg-gray-900 dark:border-gray-700">
<svg id="iconSun" class="h-5 w-5 hidden" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2m0 14v2m9-9h-2M5 12H3m15.364-6.364l-1.414 1.414M7.05 16.95l-1.414 1.414m0-11.314l1.414 1.414M16.95 16.95l1.414 1.414"/><circle cx="12" cy="12" r="4"/></svg>
<svg id="iconMoon" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg>
</button>
</div>
</div>
</div>
</header>
<!-- Hero Search -->
<section class="relative">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="grid lg:grid-cols-2 gap-10 py-12 lg:py-16 items-center">
<div>
<p class="inline-flex items-center gap-2 rounded-full border border-brand-200 bg-white/80 px-3 py-1 text-xs text-brand-700 shadow-sm dark:bg-gray-900/70 dark:border-brand-800 dark:text-brand-300">
<span class="h-2 w-2 rounded-full bg-brand-500"></span> Support & Downloads
</p>
<h1 class="mt-4 text-3xl sm:text-4xl font-bold tracking-tight">Find manuals, firmware, KB and videos</h1>
<p class="mt-3 text-base text-gray-600 dark:text-gray-300 max-w-2xl">Search by <strong>model</strong> or <strong>serial number</strong>. Results are linked to the selected product. Internal items require employee access.</p>
<form id="searchForm" class="mt-6">
<label class="relative block">
<input id="q" type="text" placeholder="e.g. A1 or SN-A1-2025-…" class="w-full rounded-xl border border-gray-300 bg-white px-4 py-4 pr-28 text-base shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 dark:bg-gray-950 dark:border-gray-800" />
<button class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm hover:border-brand-500 dark:bg-gray-900 dark:border-gray-700" type="submit" aria-label="Search">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 18a7 7 0 100-14 7 7 0 000 14z"/></svg>
</button>
</label>
<p class="mt-2 text-xs text-gray-500">Tip: Paste a serial number to autoresolve its model.</p>
</form>
<div class="mt-6 grid grid-cols-2 gap-3 text-sm">
<button data-example="A1" class="rounded-xl border border-gray-200 p-3 hover:border-brand-500 dark:border-gray-800">Try: A1</button>
<button data-example="SN-A1-2025-0001" class="rounded-xl border border-gray-200 p-3 hover:border-brand-500 dark:border-gray-800">Try: SN-A1-2025-0001</button>
</div>
</div>
<div class="relative">
<div class="relative rounded-2xl border border-gray-200 bg-white p-3 shadow-soft dark:bg-gray-900 dark:border-gray-800">
<div class="aspect-video w-full overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800">
<img src="https://images.pexels.com/photos/280140/pexels-photo-280140.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=1600" alt="Construction cranes — Pexels" class="h-full w-full object-cover"/>
</div>
<div class="mt-3 flex items-center justify-between">
<div class="text-sm text-gray-600 dark:text-gray-300">2min overview: wiring & installation</div>
<a href="https://www.pexels.com/video/time-lapse-video-of-an-excavator-5342014/" target="_blank" rel="noopener" class="inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm border border-gray-300 hover:border-brand-500 dark:border-gray-700">
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.26a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/></svg>
Watch on Pexels
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Sticky Filters -->
<div id="filters" class="sticky top-[64px] z-40 border-y border-gray-200/70 bg-white/80 backdrop-blur supports-[backdrop-filter]:bg-white/60 dark:bg-gray-950/70 dark:border-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-3">
<div class="flex flex-wrap items-center gap-2 text-sm">
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="doc" checked> Documents</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="sw" checked> Software</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="kb" checked> Knowledge</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" class="accent-brand-600" data-type="vid" checked> Videos</label>
<span class="h-5 w-px bg-gray-300/60 dark:bg-gray-700"></span>
<select id="langFilter" class="rounded-lg border border-gray-300 bg-white px-2.5 py-1.5 dark:bg-gray-950 dark:border-gray-800"><option value="">All languages</option><option value="en">EN</option><option value="zh">ZH</option><option value="it">IT</option></select>
<select id="platFilter" class="rounded-lg border border-gray-300 bg-white px-2.5 py-1.5 dark:bg-gray-950 dark:border-gray-800"><option value="">All platforms</option><option value="win">Windows</option><option value="mac">macOS</option><option value="linux">Linux</option><option value="all">All</option></select>
<span class="ml-auto text-xs text-gray-500">Employee visibility: <span id="empVis">Public only</span></span>
</div>
</div>
</div>
<!-- Product card and Results -->
<section class="py-10">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<!-- Selected product card -->
<div id="productCard" class="hidden rounded-2xl border border-gray-200 bg-white p-6 shadow-soft dark:bg-gray-900 dark:border-gray-800"></div>
<!-- Empty state / Latest updates -->
<div id="emptyState" class="mt-6">
<h2 class="text-xl sm:text-2xl font-semibold">Latest updates</h2>
<div id="latestGrid" class="mt-4 grid gap-6 lg:grid-cols-3"></div>
</div>
<!-- Results lists -->
<div id="results" class="mt-8 space-y-10 hidden">
<div id="docSection">
<div class="flex items-end justify-between gap-4">
<h3 class="text-lg font-semibold">Manuals & documents</h3>
<div class="text-xs text-gray-500">Language filter applies</div>
</div>
<div id="docList" class="mt-4 grid gap-4"></div>
</div>
<div id="swSection" class="border-t border-gray-200/70 pt-8 dark:border-gray-800">
<div class="flex items-end justify-between gap-4">
<h3 class="text-lg font-semibold">Software & firmware</h3>
<div class="text-xs text-gray-500">Platform filter applies</div>
</div>
<div id="swList" class="mt-4 grid gap-4"></div>
</div>
<div id="kbSection" class="border-t border-gray-200/70 pt-8 dark:border-gray-800">
<h3 class="text-lg font-semibold">Knowledge base</h3>
<div id="kbList" class="mt-4 grid gap-4 lg:grid-cols-2"></div>
</div>
<div id="vidSection" class="border-t border-gray-200/70 pt-8 dark:border-gray-800">
<div class="flex items-end justify-between gap-4">
<h3 class="text-lg font-semibold">Videos</h3>
<div class="text-xs text-gray-500">Demo player — replace with HLS in production</div>
</div>
<div id="videoList" class="mt-4 grid gap-6 lg:grid-cols-2"></div>
</div>
</div>
</div>
</section>
<!-- Version Drawer -->
<div id="drawer" class="fixed inset-0 z-[60] hidden">
<div class="absolute inset-0 bg-black/40" onclick="toggleDrawer(false)"></div>
<aside class="absolute right-0 top-0 h-full w-full max-w-md bg-white dark:bg-gray-950 shadow-xl p-6 overflow-y-auto">
<div class="flex items-center justify-between">
<h4 class="font-semibold">Version history</h4>
<button class="rounded-lg border border-gray-300 px-2 py-1 text-sm dark:border-gray-700" onclick="toggleDrawer(false)">Close</button>
</div>
<div id="drawerBody" class="mt-4 space-y-4 text-sm"></div>
</aside>
</div>
<!-- Firmware Risk Modal -->
<div id="risk" class="fixed inset-0 z-[70] hidden">
<div class="absolute inset-0 bg-black/40" onclick="toggleRisk(false)"></div>
<div class="absolute left-1/2 top-1/2 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-gray-200 bg-white p-6 shadow-2xl dark:bg-gray-900 dark:border-gray-800">
<h4 class="font-semibold">Firmware download confirmation</h4>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">Confirm you have checked compatibility and rollback instructions. Incorrect firmware may brick the device.</p>
<label class="mt-4 inline-flex items-center gap-2 text-sm"><input type="checkbox" id="riskAck" class="accent-brand-600"> I understand the risk</label>
<div class="mt-4 flex justify-end gap-2">
<button class="rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700" onclick="toggleRisk(false)">Cancel</button>
<button id="riskGo" class="rounded-lg bg-brand-600 px-3 py-1.5 text-sm text-white disabled:opacity-50" disabled>Download</button>
</div>
</div>
</div>
<!-- Footer -->
<footer class="mt-12 border-t border-gray-200/70 dark:border-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-10">
<div class="grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
<div>
<div class="flex items-center gap-2">
<span class="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-brand-600 text-white shadow-soft">L</span>
<span class="font-semibold">Leitan GPS</span>
</div>
<p class="mt-3 text-sm text-gray-600 dark:text-gray-300">Support & Downloads for construction machinery GPS automation.</p>
</div>
<div>
<div class="font-semibold">Product</div>
<ul class="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
<li><a href="/products/index.html" class="hover:underline">Catalog</a></li>
<li><a href="/employee/index.html" class="hover:underline">Employee portal</a></li>
</ul>
</div>
<div>
<div class="font-semibold">Resources</div>
<ul class="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
<li><a href="#" class="hover:underline">Privacy policy</a></li>
<li><a href="#" class="hover:underline">Cookie preferences</a></li>
</ul>
</div>
<div>
<div class="font-semibold">Contact</div>
<ul class="mt-3 space-y-2 text-sm text-gray-600 dark:text-gray-300">
<li>support@example.com</li>
<li>MonFri, 09:0018:00</li>
<li>Italy / Singapore</li>
</ul>
</div>
</div>
<div class="mt-8 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-gray-500">
<div>© 2025 Leitan Tech. All rights reserved.</div>
<a href="#top" class="inline-flex items-center gap-1 hover:text-brand-600 dark:hover:text-brand-400">Back to top <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></a>
</div>
</div>
</footer>
<!-- Scripts -->
<script>
// Theme init
(function(){ const s=localStorage.getItem('theme'); const d=window.matchMedia('(prefers-color-scheme: dark)').matches; if (s==='dark' || (!s && d)) document.documentElement.classList.add('dark'); })();
const themeBtn=document.getElementById('themeToggle'); const sun=document.getElementById('iconSun'); const moon=document.getElementById('iconMoon');
function syncIcons(){ const d=document.documentElement.classList.contains('dark'); sun?.classList.toggle('hidden',!d); moon?.classList.toggle('hidden',d); }
themeBtn?.addEventListener('click',()=>{ document.documentElement.classList.toggle('dark'); localStorage.setItem('theme', document.documentElement.classList.contains('dark')?'dark':'light'); syncIcons(); }); syncIcons();
// Employee mode
const isEmp = !!localStorage.getItem('employee');
const empBadge = document.getElementById('empBadge');
empBadge.textContent = isEmp ? 'Employee mode' : 'Visitor mode';
empBadge.classList.remove('hidden');
document.getElementById('empVis').textContent = isEmp ? 'Public + Internal' : 'Public only';
// Demo datasets (normally load /data/*.json)
const PRODUCTS=[
{id:'A1', model:'A1', category:'Positioning unit', snPrefixes:['SN-A1-','A1-2025-'], specs:{Accuracy:'RTK cm-level',Interfaces:'CAN/RS485',Ingress:'IP67'}},
{id:'IMU-PRO', model:'IMU-PRO', category:'Sensor (IMU)', snPrefixes:['SN-IMU-','IMU-2025-'], specs:{Axes:'6-axis',Bias:'Low drift',Ingress:'IP65'}},
{id:'AX-5', model:'AX-5', category:'Antenna', snPrefixes:['SN-AX5-'], specs:{Type:'GNSS L1/L2',Mount:'Magnetic',Ingress:'IP66'}}
];
const DOCS=[
{id:'doc-001', productId:'A1', title:'A1 Wiring & Installation', type:'manual', version:'v1.8', lang:'en', size:'2.3MB', updatedAt:'2025-07-08', checksum:'SHA256-…d9c', url:'#', visibility:'public'},
{id:'doc-002', productId:'A1', title:'A1 Service Manual', type:'service', version:'v1.7', lang:'en', size:'4.7MB', updatedAt:'2025-07-22', checksum:'SHA256-…9ab', url:'#', visibility:'internal'},
{id:'doc-010', productId:'IMU-PRO', title:'IMUPRO Datasheet', type:'datasheet', version:'v3.0', lang:'en', size:'1.1MB', updatedAt:'2025-06-30', checksum:'SHA256-…ab2', url:'#', visibility:'public'}
];
const SW=[
{id:'sw-101', productId:'A1', title:'A1 Config Tool', kind:'software', version:'v2.2.0', platform:'win', size:'18.4MB', updatedAt:'2025-06-20', checksum:'SHA256-…6a5', notesUrl:'#', compat:'A1 hw v2.x+', url:'#', visibility:'public', risk:false},
{id:'sw-102', productId:'A1', title:'A1 Firmware', kind:'firmware', version:'v2.3.1', platform:'all', size:'21.0MB', updatedAt:'2025-08-02', checksum:'MD5-…1c3', notesUrl:'#', compat:'A1 hw v2.x+', url:'#', visibility:'internal', risk:true},
{id:'sw-210', productId:'IMU-PRO', title:'IMUPRO Tool', kind:'software', version:'v1.4.2', platform:'win', size:'11.2MB', updatedAt:'2025-07-10', checksum:'SHA256-…9ee', notesUrl:'#', compat:'IMUPRO all', url:'#', visibility:'public', risk:false}
];
const KB=[
{id:'kb-301', productId:'A1', title:'Position drift under vibration — checklist', summary:'Mounting, grounding, EMI, power ripple…', tags:['Troubleshooting','Installation'], url:'#', visibility:'internal'},
{id:'kb-302', productId:'A1', title:'Quick start — first fix in 60s', summary:'A minimal path to first RTK fix.', tags:['Quickstart'], url:'#', visibility:'public'}
];
const VIDS=[
{id:'vid-501', productId:'A1', title:'A1 quick install (2:13)', duration:'02:13', poster:'https://images.pexels.com/photos/14452156/pexels-photo-14452156.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=1600', sources:[{src:'/media/a1-install-720.mp4',label:'720p'}], visibility:'public'},
{id:'vid-502', productId:'A1', title:'Internal: wiring pitfalls (3:40)', duration:'03:40', poster:'https://images.pexels.com/photos/159306/pexels-photo-159306.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=1600', sources:[{src:'/media/a1-internal-720.mp4',label:'720p'}], visibility:'internal'}
];
// Resolve query to product
const params=new URLSearchParams(location.search); const q=params.get('q')||''; if(q) document.getElementById('q').value=q;
function resolveProduct(input){ if(!input) return null; const s=input.trim().toUpperCase();
// serial match
for(const p of PRODUCTS){ if(p.snPrefixes?.some(pre=>s.startsWith(pre.toUpperCase()))) return p; }
// model exact or prefix
return PRODUCTS.find(p=>p.model.toUpperCase()===s || s.startsWith(p.model.toUpperCase()))||null;
}
// UI helpers
function badgeInternal(v){ return v==='internal'?'<span class="inline-flex items-center gap-1 rounded bg-gray-100 px-2 py-1 text-[11px] text-gray-600 dark:bg-gray-800 dark:text-gray-300">\uD83D\uDD12 Internal</span>':''; }
function lockCls(v){ return v==='internal' && !isEmp ? 'opacity-40 pointer-events-none' : ''; }
// Render product card
let CURRENT=null;
function renderProductCard(p){ if(!p){ document.getElementById('productCard').classList.add('hidden'); return; }
const specs=Object.entries(p.specs||{}).map(([k,v])=>`<div class='rounded-xl border border-gray-200 p-3 dark:border-gray-800'><dt class='text-gray-500'>${k}</dt><dd class='font-medium'>${v}</dd></div>`).join('');
document.getElementById('productCard').innerHTML=`
<div class='flex flex-col md:flex-row items-start md:items-center justify-between gap-4'>
<div>
<div class='text-sm text-gray-500'>${p.category}</div>
<h2 class='mt-1 text-2xl font-semibold'>${p.model}</h2>
<div class='mt-1 text-xs text-gray-500'>SN prefixes: ${(p.snPrefixes||[]).join(', ')||'—'}</div>
</div>
<div class='grid grid-cols-2 sm:grid-cols-3 gap-3 text-sm'>${specs}</div>
<div class='flex gap-2'>
<a class='rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700' href='/p/${encodeURIComponent(p.model.toLowerCase())}.html?model=${encodeURIComponent(p.model)}'>Open product page</a>
</div>
</div>`;
document.getElementById('productCard').classList.remove('hidden');
}
// Render latest updates (when no selection)
function renderLatest(){ const all=[...DOCS,...SW,...KB,...VIDS]; all.sort((a,b)=>new Date(b.updatedAt||'1970-01-01')-new Date(a.updatedAt||'1970-01-01')); const top=all.slice(0,3);
document.getElementById('latestGrid').innerHTML=top.map(x=>`
<article class='rounded-2xl border border-gray-200 bg-white p-6 shadow-soft dark:bg-gray-900 dark:border-gray-800'>
<div class='text-xs text-gray-500'>${x.kind||x.type||'Resource'} · ${x.updatedAt||'—'}</div>
<h3 class='mt-1 font-semibold'>${x.title||x.model}</h3>
<p class='mt-1 text-sm text-gray-600 dark:text-gray-300'>${x.size?x.size+' · ':''}${x.version?x.version+' · ':''}${x.platform?x.platform.toUpperCase()+' · ':''}${x.checksum||''}</p>
<div class='mt-3 flex gap-2'>
<span>${badgeInternal(x.visibility||'public')}</span>
<button class='rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700' onclick='document.getElementById("q").value="A1"; startSearch();'>View</button>
</div>
</article>`).join('');
}
// Render results for selected product
function renderResults(){ const p=CURRENT; if(!p) return; const lang=document.getElementById('langFilter').value; const plat=document.getElementById('platFilter').value;
// filter toggles
const chosenTypes=[...document.querySelectorAll('#filters input[type=checkbox][data-type]:checked')].map(el=>el.getAttribute('data-type'));
// docs
const docs=DOCS.filter(d=>d.productId===p.id && (!lang||d.lang===lang));
document.getElementById('docSection').classList.toggle('hidden',!chosenTypes.includes('doc'));
document.getElementById('docList').innerHTML=docs.map(d=>`
<div class='flex flex-col sm:flex-row items-start sm:items-center gap-4 rounded-2xl border border-gray-200 bg-white p-4 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(d.visibility)}'>
<div class='flex min-w-0 flex-1 items-center gap-4'>
<div class='flex h-12 w-12 items-center justify-center rounded-xl bg-brand-50 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300'>
<svg class='h-6 w-6' fill='none' stroke='currentColor' stroke-width='1.5' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' d='M12 6v6m0 0l-3-3m3 3l3-3M4 18h16'/></svg>
</div>
<div class='min-w-0'>
<div class='font-medium truncate'>${d.title} (${(d.version||'').toUpperCase()} · ${(d.lang||'').toUpperCase()})</div>
<div class='text-xs text-gray-500'>${d.type} · ${d.size||'—'} · Updated: ${d.updatedAt||'—'} · ${d.checksum||''}</div>
</div>
</div>
<div class='flex shrink-0 items-center gap-3'>
${badgeInternal(d.visibility)}
<a href='${d.url||'#'}' class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700'>Download</a>
<button class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700' onclick='openDrawer("${d.id}")'>Version history</button>
</div>
</div>`).join('');
// sw
const sw=SW.filter(s=>s.productId===p.id && (!plat||s.platform===plat||s.platform==='all'));
document.getElementById('swSection').classList.toggle('hidden',!chosenTypes.includes('sw'));
document.getElementById('swList').innerHTML=sw.map(s=>`
<div class='flex flex-col sm:flex-row items-start sm:items-center gap-4 rounded-2xl border border-gray-200 bg-white p-4 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(s.visibility)}'>
<div class='flex min-w-0 flex-1 items-center gap-4'>
<div class='flex h-12 w-12 items-center justify-center rounded-xl ${s.kind==='firmware'?'bg-amber-50 text-amber-700 dark:bg-amber-900/20 dark:text-amber-300':'bg-brand-50 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300'}'>
<svg class='h-6 w-6' fill='none' stroke='currentColor' stroke-width='1.5' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' d='M4 4h16v12H4z'/><path stroke-linecap='round' stroke-linejoin='round' d='M4 20h16'/></svg>
</div>
<div class='min-w-0'>
<div class='font-medium truncate'>${s.title} (${s.version}) — ${s.kind}</div>
<div class='text-xs text-gray-500'>${(s.platform||'').toUpperCase()} · ${s.size||'—'} · Updated: ${s.updatedAt||'—'} · ${s.checksum||''} · Compat: ${s.compat||'—'}</div>
</div>
</div>
<div class='flex shrink-0 items-center gap-3'>
${badgeInternal(s.visibility)}
<a href='${s.notesUrl||'#'}' class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700'>Release notes</a>
<button class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700' onclick='${s.kind==='firmware'?'openRisk()':'alert("Download started (demo)")'}'>Download</button>
</div>
</div>`).join('');
// kb
const kb=KB.filter(k=>k.productId===p.id);
document.getElementById('kbSection').classList.toggle('hidden',!chosenTypes.includes('kb'));
document.getElementById('kbList').innerHTML=kb.map(k=>`
<article class='rounded-2xl border border-gray-200 bg-white p-6 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(k.visibility)}'>
<div class='text-xs text-gray-500'>${(k.tags||[]).join(' · ')}</div>
<h4 class='mt-1 font-semibold'>${k.title}</h4>
<p class='mt-1 text-sm text-gray-600 dark:text-gray-300'>${k.summary||''}</p>
<div class='mt-3 flex items-center gap-3'>${badgeInternal(k.visibility)}<a href='${k.url||'#'}' class='inline-flex items-center gap-1 rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:border-brand-500 dark:border-gray-700'>View</a></div>
</article>`).join('');
// videos
const vids=VIDS.filter(v=>v.productId===p.id);
document.getElementById('vidSection').classList.toggle('hidden',!chosenTypes.includes('vid'));
document.getElementById('videoList').innerHTML=vids.map(v=>`
<div class='rounded-2xl border border-gray-200 bg-white p-3 shadow-soft dark:bg-gray-900 dark:border-gray-800 ${lockCls(v.visibility)}'>
<div class='aspect-video w-full overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800'>
<video controls preload='metadata' poster='${v.poster||''}' class='h-full w-full'>${(v.sources||[]).map(s=>`<source src='${s.src}' type='video/mp4'/>`).join('')}</video>
</div>
<div class='mt-3 flex items-center justify-between'>
<div class='text-sm text-gray-600 dark:text-gray-300'>${v.title||''}</div>
${badgeInternal(v.visibility)}
</div>
</div>`).join('');
document.getElementById('emptyState').classList.add('hidden');
document.getElementById('results').classList.remove('hidden');
}
function startSearch(){ const input=document.getElementById('q').value; const p=resolveProduct(input); if(!p){ alert('No matching product. Try A1'); return; } CURRENT=p; renderProductCard(p); renderResults(); }
// Filters change
document.querySelectorAll('#filters input[type=checkbox][data-type]').forEach(el=>el.addEventListener('change',()=>CURRENT&&renderResults()));
document.getElementById('langFilter').addEventListener('change',()=>CURRENT&&renderResults());
document.getElementById('platFilter').addEventListener('change',()=>CURRENT&&renderResults());
// Search submit
document.getElementById('searchForm').addEventListener('submit',e=>{e.preventDefault(); startSearch();});
document.querySelectorAll('[data-example]').forEach(btn=>btn.addEventListener('click',()=>{document.getElementById('q').value=btn.getAttribute('data-example'); startSearch();}));
// Init with q param
if(q){ startSearch(); }
renderLatest();
// Drawer / Risk modal controls
function toggleDrawer(v){ document.getElementById('drawer').classList.toggle('hidden',!v); }
window.openDrawer=function(id){ const list=[{version:'v1.8',date:'2025-07-08',changes:['Updated wiring pinout','Added IT manual']},{version:'v1.7',date:'2025-04-12',changes:['Fix typos','New CE appendix']}];
document.getElementById('drawerBody').innerHTML=list.map(x=>`<div class='rounded-xl border border-gray-200 p-4 dark:border-gray-800'><div class='font-medium'>${x.version} · ${x.date}</div><ul class='mt-2 list-disc pl-5 text-gray-600 dark:text-gray-300'>${x.changes.map(c=>`<li>${c}</li>`).join('')}</ul><div class='mt-3'><a href='#' class='rounded-lg border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-700'>Download</a></div></div>`).join('');
toggleDrawer(true);
}
function toggleRisk(v){ document.getElementById('risk').classList.toggle('hidden',!v); document.getElementById('riskAck').checked=false; document.getElementById('riskGo').disabled=true; }
window.openRisk=function(){ toggleRisk(true); }
document.getElementById('riskAck').addEventListener('change',e=>{ document.getElementById('riskGo').disabled=!e.target.checked; });
document.getElementById('riskGo').addEventListener('click',()=>{ alert('Download started (demo)'); toggleRisk(false); });
</script>
</body>
</html>