SignLens / index.html
venmugilrajan's picture
Upload index.html with huggingface_hub
1c696aa verified
Raw History Blame Contribute Delete
30.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark" />
<title>SignLens</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css?v=27" />
<!-- Three.js pinned local importmap (no CDN) -->
<script type="importmap">
{
"imports": {
"three": "./vendor/three/three.module.js"
}
}
</script>
<script>
// Feature flag: set window.USE_3D_AVATAR = false in the browser console to revert to 2D
window.USE_3D_AVATAR = true;
</script>
</head>
<body>
<!-- โ•โ• TOP BAR โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<header class="topbar">
<div class="brand">
<!-- Single geometric mark: three ascending strokes, a hand reading as signal. -->
<svg class="brand-mark" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<rect x="3" y="13" width="4" height="8" rx="2" />
<rect x="10" y="7" width="4" height="14" rx="2" />
<rect x="17" y="3" width="4" height="18" rx="2" />
</svg>
<span class="brand-name">SignLens</span>
</div>
<div class="mode-toggle" id="mode-toggle" role="group" aria-label="Sign language">
<button class="mode-btn active" data-mode="asl" id="btn-asl" aria-pressed="true">ASL</button>
<button class="mode-btn" data-mode="isl" id="btn-isl" aria-pressed="false">ISL</button>
<button class="mode-btn" data-mode="visually" id="btn-visually" aria-pressed="false">VISUALLY</button>
<button class="mode-btn" data-mode="upload" id="btn-upload" aria-pressed="false">UPLOAD</button>
<span class="mode-slider" aria-hidden="true"></span>
</div>
<div class="topbar-right">
<button class="ghost-btn guide-btn" popovertarget="panel-guide" id="btn-open-guide" aria-label="Open Sign Guide">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -2px; margin-right: 5px;"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg><span>Sign Guide</span>
</button>
<button class="ghost-btn" id="btn-open-settings" popovertarget="panel-settings">Settings</button>
<button class="ghost-btn" id="btn-open-shortcuts" popovertarget="panel-help">Shortcuts</button>
<button class="ghost-btn theme-toggle-btn" id="btn-theme-toggle" aria-label="Toggle Light or Dark Mode" title="Toggle Light / Dark Mode">
<span id="theme-toggle-icon" aria-hidden="true">โ˜€๏ธ</span>
</button>
<div class="status-pill" id="status-pill">
<span class="status-dot" aria-hidden="true"></span>
<span id="status-text" role="status" aria-live="polite">Connecting</span>
</div>
</div>
</header>
<!-- โ•โ• WORKSPACE (LIVE CAMERA) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<main class="workspace" id="live-workspace">
<!-- โ”€โ”€ Camera stage โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -->
<section class="stage" aria-label="Camera">
<div class="viewport">
<video id="webcam-video" autoplay playsinline muted></video>
<canvas id="display-canvas"></canvas>
<div class="no-hand-overlay visible" id="no-hand-overlay">
<div class="idle-title">Show your hand to the camera</div>
<div class="idle-sub">Tracking starts as soon as a hand is in frame.</div>
</div>
</div>
<!-- Live readout: the one thing the user actually watches -->
<div class="readout">
<div class="readout-dial">
<div class="dial-ring" aria-hidden="true"></div>
<div class="detected-letter" id="detected-letter" role="status" aria-live="polite" aria-atomic="true"></div>
</div>
<div class="readout-body">
<div class="readout-line">
<span class="detected-sub" id="detected-conf">Waiting</span>
<span class="hand-badge" id="hand-badge" role="status" aria-live="polite">NO HAND</span>
</div>
<div class="streak-row">
<div class="streak-track"><div class="streak-fill" id="streak-bar"></div></div>
<span class="streak-label" id="streak-label">0 / 5</span>
</div>
</div>
<div class="stage-actions">
<button class="ctrl-btn" id="btn-mirror-toggle" title="Switch between mirror selfie view and normal non-mirrored view">๐Ÿชž Mirror: On</button>
<button class="ctrl-btn" popovertarget="panel-guide" id="stage-guide-btn" title="View official sign chart">Guide</button>
<button class="ctrl-btn" id="btn-pause-detection">Pause</button>
<button class="ctrl-btn active-btn" id="btn-camera-toggle">Stop Camera</button>
</div>
</div>
</section>
<!-- โ”€โ”€ Output rail โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -->
<aside class="rail">
<article class="panel" id="word-panel">
<header class="panel-head">
<h2 class="panel-title">Building word</h2>
</header>
<div class="word-display" id="word-display">
<span class="ph">Start signing</span>
</div>
<div class="panel-actions">
<button class="btn" id="btn-backspace">Delete</button>
<button class="btn" id="btn-add-space">Next word</button>
<button class="btn btn-quiet" id="btn-clear-word">Clear</button>
</div>
</article>
<article class="panel panel-grow" id="sentence-panel">
<header class="panel-head">
<h2 class="panel-title">Sentence</h2>
<div class="panel-head-actions">
<div class="lang-dropdown-wrapper" id="lang-dropdown-wrapper">
<button type="button" class="lang-picker-btn" id="lang-picker-btn" aria-haspopup="listbox" aria-expanded="false" title="Choose translation language">
<span class="lang-picker-icon">๐ŸŒ</span>
<span class="lang-picker-label" id="lang-picker-current-label">English</span>
<svg class="lang-picker-chevron" viewBox="0 0 20 20" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 8 10 12 14 8"></polyline>
</svg>
</button>
<div class="lang-menu" id="lang-menu" role="listbox" aria-label="Select translation language">
<div class="lang-menu-header">Translate Sentence To</div>
<div class="lang-menu-list">
<button type="button" class="lang-item is-selected" data-lang="en">
<span class="lang-name">English</span>
<span class="lang-sub">Original</span>
</button>
<div class="lang-menu-divider"></div>
<button type="button" class="lang-item" data-lang="ta">
<span class="lang-name">Tamil</span>
<span class="lang-sub">เฎคเฎฎเฎฟเฎดเฏ</span>
</button>
<button type="button" class="lang-item" data-lang="hi">
<span class="lang-name">Hindi</span>
<span class="lang-sub">เคนเคฟเคจเฅเคฆเฅ€</span>
</button>
<button type="button" class="lang-item" data-lang="te">
<span class="lang-name">Telugu</span>
<span class="lang-sub">เฐคเฑ†เฐฒเฑเฐ—เฑ</span>
</button>
<button type="button" class="lang-item" data-lang="ml">
<span class="lang-name">Malayalam</span>
<span class="lang-sub">เดฎเดฒเดฏเดพเดณเด‚</span>
</button>
<button type="button" class="lang-item" data-lang="kn">
<span class="lang-name">Kannada</span>
<span class="lang-sub">เฒ•เฒจเณเฒจเฒก</span>
</button>
<button type="button" class="lang-item" data-lang="es">
<span class="lang-name">Spanish</span>
<span class="lang-sub">Espaรฑol</span>
</button>
<button type="button" class="lang-item" data-lang="fr">
<span class="lang-name">French</span>
<span class="lang-sub">Franรงais</span>
</button>
<button type="button" class="lang-item" data-lang="de">
<span class="lang-name">German</span>
<span class="lang-sub">Deutsch</span>
</button>
<button type="button" class="lang-item" data-lang="ja">
<span class="lang-name">Japanese</span>
<span class="lang-sub">ๆ—ฅๆœฌ่ชž</span>
</button>
<button type="button" class="lang-item" data-lang="zh">
<span class="lang-name">Chinese</span>
<span class="lang-sub">ไธญๆ–‡</span>
</button>
<button type="button" class="lang-item" data-lang="ar">
<span class="lang-name">Arabic</span>
<span class="lang-sub">ุงู„ุนุฑุจูŠุฉ</span>
</button>
</div>
</div>
</div>
<button class="btn btn-sm" id="btn-speak">Speak</button>
<button class="btn btn-sm" id="btn-copy">Copy</button>
</div>
</header>
<div class="sentence-display is-single" id="sentence-display" role="status" aria-live="polite">
<!-- Left Side: English Original -->
<div class="sentence-col sentence-col-orig" id="sentence-col-orig">
<div class="sentence-col-header" id="sentence-orig-header">
<span class="sentence-col-tag">English (Original)</span>
</div>
<div class="sentence-col-body" id="sentence-original-text">
<em class="ph">Your sentence will appear here</em>
</div>
</div>
<!-- Right Side: Selected Translated Language (shown only when a translated language is selected) -->
<div class="sentence-col sentence-col-trans" id="translation-box" style="display: none;">
<div class="sentence-col-header">
<span class="sentence-col-tag" id="translation-lang-tag">Translation</span>
<span class="translation-status" id="translation-status"></span>
</div>
<div class="sentence-col-body translation-text" id="translation-text"></div>
</div>
</div>
<div class="word-chips" id="word-chips"></div>
<button class="btn btn-danger" id="btn-clear-sentence">Clear all</button>
</article>
</aside>
</main>
<!-- โ•โ• VISUALLY (PRACTICE STUDIO) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<section class="workspace visually-workspace hidden" id="visually-workspace" aria-label="Practice Studio">
<!-- Left Column: Input, Speech Dictation, and Sequence Breakdown -->
<aside class="practice-input-rail">
<article class="panel panel-grow practice-panel">
<header class="panel-head">
<div class="practice-head-title">
<span class="practice-badge">PRACTICE STUDIO</span>
<h2 class="panel-title">Text to Sign</h2>
</div>
<div class="practice-dialect-toggle" id="practice-dialect-toggle">
<button class="dialect-pill active" id="btn-practice-asl">ASL</button>
<button class="dialect-pill" id="btn-practice-isl">ISL</button>
</div>
</header>
<!-- Textarea with integrated voice mic & actions -->
<div class="practice-textarea-box">
<textarea id="practice-input" placeholder="Type words or sentence (e.g. 'HI BABY', 'HOW ARE YOU')..." rows="4"></textarea>
<div class="practice-input-tools">
<button class="tool-btn mic-btn" id="btn-mic" title="Voice Input (Speech-to-Sign)">
<svg class="icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5-3c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
</svg>
<span id="mic-label">Speak</span>
</button>
<button class="tool-btn" id="btn-speak-input" title="Speak text aloud">
<svg class="icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
</svg>
</button>
<button class="tool-btn" id="btn-clear-practice" title="Clear text">Clear</button>
<span class="char-count" id="practice-char-count">0/200</span>
</div>
</div>
<!-- Quick sample prompts -->
<div class="quick-samples">
<span class="quick-label">Try:</span>
<button class="sample-chip" data-text="HI">HI</button>
<button class="sample-chip" data-text="HELLO">HELLO</button>
<button class="sample-chip" data-text="THANK YOU">THANK YOU</button>
<button class="sample-chip" data-text="HOW ARE YOU">HOW ARE YOU</button>
<button class="sample-chip" data-text="BABY">BABY</button>
</div>
<!-- Main Trigger & Speed -->
<div class="practice-action-row">
<button class="btn btn-primary btn-demo" id="btn-start-sign">
<svg class="icon-play" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
<span>Demonstrate Sign</span>
</button>
<div class="speed-control">
<span class="speed-label">Speed</span>
<button class="speed-btn" data-speed="0.5">0.5x</button>
<button class="speed-btn active" data-speed="1.0">1.0x</button>
<button class="speed-btn" data-speed="1.5">1.5x</button>
</div>
</div>
<!-- Letter Tape Progression -->
<div class="sequence-section">
<div class="sequence-head">
<span class="seq-title">Sign Sequence Timeline</span>
<span class="seq-counter" id="seq-counter">0 / 0</span>
</div>
<div class="sequence-chips" id="sequence-chips">
<span class="ph">Enter text above to preview gesture timelineโ€ฆ</span>
</div>
</div>
</article>
</aside>
<!-- Right Column: Interactive 3D Avatar Skeleton Stage -->
<section class="practice-stage" aria-label="Avatar Stage">
<div class="avatar-viewport">
<canvas id="avatar-canvas-3d"></canvas>
<canvas id="avatar-canvas"></canvas>
<!-- 3D Avatar Loading Indicator Overlay -->
<div class="avatar-3d-loading-overlay" id="avatar-3d-loading-overlay">
<div class="avatar-loading-box">
<div class="avatar-loading-spinner-ring">
<div class="avatar-loading-core-dot"></div>
</div>
<div class="avatar-loading-title">Initializing 3D Sign Studio</div>
<div class="avatar-loading-subtitle" id="avatar-loading-status">Downloading 3D avatar assetsโ€ฆ</div>
<div class="avatar-loading-progress-track">
<div class="avatar-loading-progress-bar" id="avatar-loading-bar"></div>
</div>
</div>
</div>
<!-- Active Letter Glow Badge -->
<div class="avatar-hud-letter" id="avatar-hud-letter">
<span class="hud-letter-char" id="hud-letter-char">โ€”</span>
<span class="hud-letter-sub" id="hud-letter-sub">Waiting for input</span>
<span class="hud-letter-cue" id="hud-letter-cue" style="display: none;"></span>
</div>
<!-- Avatar Status Badge -->
<div class="avatar-hud-status" id="avatar-hud-status">
<span class="status-badge-dot"></span>
<span id="avatar-status-text">Ready</span>
</div>
<!-- Real Human Sign Reference Card (Top-Right) -->
<div class="avatar-sign-preview" id="avatar-sign-preview" title="Official sign gesture photo reference" style="display: none;">
<div class="avatar-sign-img-wrap">
<img id="avatar-sign-img" src="" alt="Sign Gesture Reference" />
<span class="avatar-sign-badge" id="avatar-sign-badge">ISL</span>
</div>
<div class="avatar-sign-info">
<div class="avatar-sign-char-row">
<span class="avatar-sign-char" id="avatar-sign-char">A</span>
<span class="avatar-sign-hands" id="avatar-sign-hands">1 Hand</span>
</div>
<span class="avatar-sign-desc" id="avatar-sign-desc">Sign gesture reference</span>
</div>
</div>
<!-- Playback Controls Floating Bar -->
<div class="avatar-controls-bar">
<button class="avatar-ctrl-btn" id="btn-avatar-prev" title="Previous letter">โฎ</button>
<button class="avatar-ctrl-btn play-pause-btn" id="btn-avatar-playpause" title="Play/Pause">โธ</button>
<button class="avatar-ctrl-btn" id="btn-avatar-next" title="Next letter">โญ</button>
<button class="avatar-ctrl-btn" id="btn-avatar-replay" title="Replay all">โŸฒ</button>
<div class="avatar-progress-wrap">
<input type="range" class="avatar-scrubber" id="avatar-scrubber" min="0" max="100" value="0" step="0.1" />
</div>
</div>
</div>
</section>
</section>
<!-- โ•โ• UPLOAD (IMAGE SIGN RECOGNITION STUDIO) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<section class="workspace upload-workspace hidden" id="upload-workspace" aria-label="Upload Sign Recognition">
<!-- Left Column: Upload Dropzone, Controls & Sample Images -->
<aside class="upload-sidebar">
<article class="panel upload-panel">
<header class="panel-head">
<div class="upload-head-title">
<span class="upload-badge">IMAGE RECOGNITION</span>
<h2 class="panel-title">Sign Image Classifier</h2>
</div>
<div class="upload-dialect-toggle" id="upload-dialect-toggle">
<button class="upload-dialect-btn active" id="upload-dialect-asl" data-dialect="asl">ASL</button>
<button class="upload-dialect-btn" id="upload-dialect-isl" data-dialect="isl">ISL</button>
</div>
</header>
<!-- Drag & Drop Zone -->
<div class="upload-dropzone" id="upload-dropzone">
<input type="file" id="upload-file-input" accept="image/*" class="upload-file-hidden" />
<div class="dropzone-icon">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
</div>
<div class="dropzone-title">Drop your hand sign image here</div>
<div class="dropzone-sub">or <span class="dropzone-browse">browse from computer</span></div>
<div class="dropzone-formats">Supports JPG, PNG, WEBP, Camera photos</div>
</div>
<!-- Quick Test Sample Signs -->
<div class="upload-samples-wrap">
<div class="upload-samples-label">Or test with quick reference signs:</div>
<div class="upload-samples-chips" id="upload-sample-chips">
<!-- Dynamically populated or static samples -->
<button class="sample-img-chip" data-dialect="asl" data-letter="A">ASL: A</button>
<button class="sample-img-chip" data-dialect="asl" data-letter="B">ASL: B</button>
<button class="sample-img-chip" data-dialect="asl" data-letter="C">ASL: C</button>
<button class="sample-img-chip" data-dialect="asl" data-letter="L">ASL: L</button>
<button class="sample-img-chip" data-dialect="asl" data-letter="V">ASL: V</button>
<button class="sample-img-chip" data-dialect="isl" data-letter="5">ISL: 5</button>
<button class="sample-img-chip" data-dialect="isl" data-letter="C">ISL: C</button>
<button class="sample-img-chip" data-dialect="isl" data-letter="O">ISL: O</button>
</div>
</div>
<div class="upload-actions-bar">
<button class="btn btn-danger" id="btn-clear-upload" style="display: none;">Remove Image</button>
</div>
</article>
</aside>
<!-- Right Column: Visual Stage with Annotated Canvas & Prediction Results Card -->
<section class="upload-stage" aria-label="Upload Analysis Stage">
<div class="upload-viewport" id="upload-viewport">
<!-- Empty Placeholder State -->
<div class="upload-empty-state" id="upload-empty-state">
<div class="upload-empty-icon">๐Ÿ–๏ธ</div>
<h3>No Image Uploaded Yet</h3>
<p>Upload any picture with hand signs (ASL or ISL) to analyze 21 MediaPipe skeletal landmarks and classify the sign with top confidence matches.</p>
<button class="btn btn-primary" id="btn-trigger-browse">Choose Sign Image</button>
</div>
<!-- Image Canvas Container -->
<div class="upload-canvas-wrap hidden" id="upload-canvas-wrap">
<canvas id="upload-canvas"></canvas>
</div>
<!-- Prediction Floating HUD Overlay -->
<div class="upload-result-hud hidden" id="upload-result-hud">
<div class="result-hud-header">
<span class="result-badge-dialect" id="result-dialect-tag">ASL</span>
<span class="result-badge-hands" id="result-hands-tag">1 Hand Detected</span>
</div>
<div class="result-main-row">
<div class="result-letter-huge" id="result-letter">โ€”</div>
<div class="result-details">
<div class="result-conf-row">
<span class="result-conf-label">Confidence</span>
<span class="result-conf-val" id="result-confidence">99.8%</span>
</div>
<div class="result-desc" id="result-desc">Detecting sign...</div>
</div>
</div>
<!-- Top 3 Probability Breakdown -->
<div class="result-top3-section">
<div class="result-top3-title">Top Model Predictions</div>
<div class="result-top3-bars" id="result-top3-bars">
<!-- Rendered dynamically -->
</div>
</div>
</div>
</div>
</section>
</section>
<!-- โ•โ• SETTINGS (top layer) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<div popover id="panel-settings" class="sheet">
<header class="sheet-head">
<h2 class="sheet-title">Detection</h2>
<div style="display: flex; gap: 8px; align-items: center;">
<button class="ghost-btn" id="btn-reset-settings" title="Reset detection settings to defaults" style="font-size: 11px; padding: 4px 8px;">Reset</button>
<button class="ghost-btn" id="btn-close-settings" popovertarget="panel-settings" popovertargetaction="hide">Close</button>
</div>
</header>
<div class="field">
<label for="set-confidence">Minimum confidence</label>
<div class="field-row">
<input type="range" id="set-confidence" min="40" max="95" step="5" value="50" />
<output class="field-value" id="out-confidence">50%</output>
</div>
<p class="field-help">Predictions below this are ignored. Raise it if letters are captured too eagerly.</p>
</div>
<div class="field">
<label for="set-streak">Hold sign duration</label>
<div class="field-row">
<input type="range" id="set-streak" min="0.5" max="6.0" step="0.5" value="1.0" />
<output class="field-value" id="out-streak">1.0s</output>
</div>
<p class="field-help">How many seconds you must hold the hand sign steady before the letter is captured.</p>
</div>
<div class="field">
<label for="set-timeout">Auto-push word delay</label>
<div class="field-row">
<input type="range" id="set-timeout" min="1.0" max="5.0" step="0.5" value="3.0" />
<output class="field-value" id="out-timeout">3.0s</output>
</div>
<p class="field-help">How long you must lower your hand before the word commits to sentence.</p>
</div>
</div>
<!-- โ•โ• SHORTCUTS (top layer) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<div popover id="panel-help" class="sheet">
<header class="sheet-head">
<h2 class="sheet-title">How it works</h2>
<button class="ghost-btn" popovertarget="panel-help" popovertargetaction="hide">Close</button>
</header>
<ol class="steps">
<li><span class="step-n">1</span> Show a hand sign to the camera.</li>
<li><span class="step-n">2</span> Hold it steady while the ring fills.</li>
<li><span class="step-n">3</span> The letter is captured and added to the word.</li>
<li><span class="step-n">4</span> Drop your hand to commit the word to the sentence.</li>
</ol>
<div class="keys">
<div class="key-row"><kbd>Space</kbd><span>Commit current word</span></div>
<div class="key-row"><kbd>Backspace</kbd><span>Delete last letter</span></div>
<div class="key-row"><kbd>1</kbd><span>Switch to ASL</span></div>
<div class="key-row"><kbd>2</kbd><span>Switch to ISL</span></div>
<div class="key-row"><kbd>3</kbd><span>Switch to Practice (VISUALLY)</span></div>
</div>
</div>
<!-- โ•โ• SIGN REFERENCE GUIDE (top layer modal) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<div popover id="panel-guide" class="sheet sheet-wide" style="display: none;">
<header class="sheet-head">
<div class="sheet-title-group">
<h2 class="sheet-title">Sign Reference Guide</h2>
<span class="sheet-sub">Official hand gestures, required hand counts & poses</span>
</div>
<div class="guide-tabs" role="tablist">
<button class="guide-tab-btn active" id="tab-guide-isl" role="tab" aria-selected="true">๐Ÿ‡ฎ๐Ÿ‡ณ ISL (Indian Sign Language)</button>
<button class="guide-tab-btn" id="tab-guide-asl" role="tab" aria-selected="false">๐Ÿ‡บ๐Ÿ‡ธ ASL (American Sign Language)</button>
</div>
<button class="ghost-btn" id="btn-close-guide" popovertarget="panel-guide" popovertargetaction="hide">Close</button>
</header>
<!-- ISL Tab Content -->
<div class="guide-tab-content" id="guide-content-isl">
<div class="guide-alert-banner">
<span class="banner-icon">โš ๏ธ</span>
<div>
<strong>Crucial Rule in ISL:</strong> 20 alphabet letters (<kbd>A, B, D, E, F, G, H, J, K, M, N, P, Q, R, S, T, W, X, Y, Z</kbd>) strictly require <strong>TWO HANDS</strong>. Only numbers <kbd>1-9</kbd> and letters <kbd>C, I, L, O, U, V</kbd> use a single hand!
</div>
</div>
<div class="guide-section">
<div class="guide-section-head">
<h3 class="guide-section-title">Single-Hand Gestures (15 Signs)</h3>
<span class="pill-badge pill-amber">1-Hand Required</span>
</div>
<div class="guide-cards-grid" id="isl-single-cards"></div>
</div>
<div class="guide-section" style="margin-top: 28px;">
<div class="guide-section-head">
<h3 class="guide-section-title">Dual-Hand Gestures (20 Signs)</h3>
<span class="pill-badge pill-blue">2-Hands Required</span>
</div>
<div class="guide-cards-grid" id="isl-dual-cards"></div>
</div>
</div>
<!-- ASL Tab Content -->
<div class="guide-tab-content hidden" id="guide-content-asl">
<div class="guide-alert-banner">
<span class="banner-icon">๐Ÿ’ก</span>
<div>
<strong>Rule in ASL:</strong> All 26 alphabet letters (<kbd>A to Z</kbd>) are performed using <strong>ONE dominant hand</strong>.
</div>
</div>
<div class="guide-section">
<div class="guide-section-head">
<h3 class="guide-section-title">ASL Alphabet (26 Signs)</h3>
<span class="pill-badge pill-amber">1-Hand Only</span>
</div>
<div class="guide-cards-grid" id="asl-cards"></div>
</div>
</div>
</div>
<!-- โ•โ• SIGN GUIDE LIGHTBOX POPUP (Top-layer native dialog) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -->
<dialog class="sign-lightbox-dialog" id="sign-lightbox-overlay">
<div class="sign-lightbox" id="sign-lightbox">
<button class="sign-lightbox-close" id="sign-lightbox-close" aria-label="Close preview">&times;</button>
<div class="sign-lightbox-img-wrap">
<img id="sign-lightbox-img" src="" alt="Sign preview" />
</div>
<div class="sign-lightbox-letter" id="sign-lightbox-letter"></div>
<span class="sign-lightbox-type" id="sign-lightbox-type"></span>
<p class="sign-lightbox-desc" id="sign-lightbox-desc"></p>
<div class="sign-lightbox-lang" id="sign-lightbox-lang"></div>
</div>
</dialog>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script type="module" src="app.js?v=39"></script>
</body>
</html>