Files
web_template/base.html
T
jared 8df14ebbe3
Lint / JS (eslint) (push) Successful in 6s
fix: polish SLA banner component — gradient fill, dismiss persistence, terminal icons
- lt-sla-p2 .lt-sla-fill: upgrade from flat amber to gradient (#FFB300 →
  #ffd740) for visual consistency with P1 red→orange fill
- lt-sla-dismiss: add transition (0.15s ease) and :focus-visible outline so
  keyboard users get a visible focus ring
- Demo dismiss: replace .remove() with hidden + sessionStorage so banners
  stay dismissed across page navigation (data-sla-id attribute wires the key)
- Demo icons: swap emoji (🔴 🟠) for terminal-style text [ ! ] / [ ~ ] —
  emoji rendering is platform-specific and breaks the monospace aesthetic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 17:27:10 -04:00

2043 lines
111 KiB
HTML
Raw 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>
<!--
LOTUSGUILD TERMINAL DESIGN SYSTEM v1.2 — base.html
Reference template showing every component and layout pattern.
This file is a STATIC DEMO. Framework-specific wiring is in:
php/ → PHP / Tinker Tickets
python/ → Flask / Jinja2 / GANDALF
node/ → Express / EJS / PULSE
To build a new app, copy one of the framework skeletons from those
sub-directories and reference base.css + base.js.
-->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#030508">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>MY APP — LotusGuild</title>
<meta name="description" content="LotusGuild infrastructure application">
<meta name="robots" content="noindex, nofollow">
<!-- =========================================================
Security headers are set server-side. CSP nonce is injected
by SecurityHeadersMiddleware (PHP) / helmet (Node) / Flask.
All <script> tags need: nonce="NONCE_PLACEHOLDER"
========================================================= -->
<!-- Fonts: JetBrains Mono (UI) + VT323 (CRT display accent) -->
<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=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=VT323&display=swap" rel="stylesheet">
<!-- Hacker template design system CSS -->
<link rel="stylesheet" href="/web_template/base.css">
<!--
App-specific CSS (override or extend after base.css):
<link rel="stylesheet" href="/assets/css/app.css">
-->
<link rel="icon" href="/assets/images/favicon.png" type="image/png">
</head>
<body>
<a class="lt-skip-link" href="#main-content">Skip to main content</a>
<!-- ===========================================================
BOOT SEQUENCE OVERLAY
Displays once per session. Remove if not desired.
data-app-name → used in the boot text banner.
=========================================================== -->
<div id="lt-boot" class="lt-boot-overlay" data-app-name="MY APP" style="display:none">
<pre id="lt-boot-text" class="lt-boot-text"></pre>
</div>
<!-- ===========================================================
HEADER
=========================================================== -->
<!-- ===========================================================
MOBILE NAV DRAWER (hidden on desktop, slides in on mobile)
=========================================================== -->
<div id="lt-nav-drawer" class="lt-nav-drawer" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Navigation menu">
<div class="lt-nav-drawer-header">
<span class="lt-brand-title">MY APP</span>
<button type="button" class="lt-nav-drawer-close" id="lt-nav-drawer-close" aria-label="Close menu"></button>
</div>
<nav class="lt-nav-drawer-links" aria-label="Mobile navigation">
<a href="/" class="lt-nav-drawer-link active" aria-current="page">Dashboard</a>
<a href="/tickets" class="lt-nav-drawer-link">Tickets</a>
<a href="/workers" class="lt-nav-drawer-link">Workers</a>
<div class="lt-nav-drawer-section">Admin</div>
<a href="/admin/templates" class="lt-nav-drawer-link lt-nav-drawer-link--indent">Templates</a>
<a href="/admin/workflow" class="lt-nav-drawer-link lt-nav-drawer-link--indent">Workflow</a>
<a href="/admin/audit-log" class="lt-nav-drawer-link lt-nav-drawer-link--indent">Audit Log</a>
<a href="/admin/api-keys" class="lt-nav-drawer-link lt-nav-drawer-link--indent">API Keys</a>
</nav>
</div>
<div id="lt-nav-overlay" class="lt-nav-drawer-overlay"></div>
<header class="lt-header">
<div class="lt-header-left">
<!-- Hamburger — visible only on tablet/mobile (CSS hides on desktop) -->
<button type="button" class="lt-menu-btn" id="lt-menu-btn" aria-label="Open navigation menu" aria-expanded="false" aria-controls="lt-nav-drawer">
<span></span><span></span><span></span>
</button>
<!-- Brand -->
<div class="lt-brand">
<span class="lt-brand-title lt-glitch" data-text="MY APP">MY APP</span>
<span class="lt-brand-subtitle">LotusGuild Infrastructure</span>
</div>
<!-- Horizontal nav links (desktop) -->
<nav class="lt-nav" aria-label="Main navigation">
<a href="/" class="lt-nav-link active">Dashboard</a>
<a href="/tickets" class="lt-nav-link">Tickets</a>
<a href="/workers" class="lt-nav-link">Workers</a>
<!-- Dropdown example (admin menu) -->
<div class="lt-nav-dropdown">
<a href="#" class="lt-nav-link">Admin ▾</a>
<ul class="lt-nav-dropdown-menu">
<li><a href="/admin/templates">Templates</a></li>
<li><a href="/admin/workflow">Workflow</a></li>
<li><a href="/admin/audit-log">Audit Log</a></li>
<li><a href="/admin/api-keys">API Keys</a></li>
</ul>
</div>
</nav>
</div>
<div class="lt-header-right">
<!-- WebSocket status indicator -->
<div class="lt-ws-status" id="lt-ws-indicator" data-state="disconnected" aria-label="WebSocket status">
<span class="lt-dot"></span><span>Offline</span>
</div>
<!-- Theme toggle -->
<button type="button" class="lt-theme-btn" id="lt-theme-btn" aria-label="Switch to light mode" title="Switch to light mode"></button>
<!-- Notifications with badge + dropdown -->
<div class="lt-notif-dropdown-wrap" id="lt-notif-bell">
<button type="button" class="lt-btn lt-btn-sm lt-notif-bell-btn" id="lt-notif-bell-btn" aria-label="Open notifications" aria-expanded="false" aria-haspopup="true" style="padding:0 0.6rem;">🔔</button>
<div class="lt-notif-panel" id="lt-notif-panel" role="region" aria-label="Notifications" aria-hidden="true">
<div class="lt-notif-panel-header">
<span>Notifications</span>
<button type="button" class="lt-notif-panel-clear" id="lt-notif-clear-all">Mark all read</button>
</div>
<div class="lt-notif-panel-list">
<div class="lt-notif-item lt-notif-item--unread" role="button" tabindex="0" aria-label="Unread: P1 alert: storage link-down, 5 min ago. Press Enter to dismiss.">
<span class="lt-notif-dot" aria-hidden="true"></span>
<div class="lt-notif-item-body">
<div class="lt-notif-item-title">P1 alert: storage link-down</div>
<div class="lt-notif-item-time">5 min ago</div>
</div>
</div>
<div class="lt-notif-item lt-notif-item--unread" role="button" tabindex="0" aria-label="Unread: Worker node-03 reconnected, 12 min ago. Press Enter to dismiss.">
<span class="lt-notif-dot" aria-hidden="true"></span>
<div class="lt-notif-item-body">
<div class="lt-notif-item-title">Worker node-03 reconnected</div>
<div class="lt-notif-item-time">12 min ago</div>
</div>
</div>
<div class="lt-notif-item lt-notif-item--unread" role="button" tabindex="0" aria-label="Unread: Export CSV complete — 42 rows, 1 hr ago. Press Enter to dismiss.">
<span class="lt-notif-dot" aria-hidden="true"></span>
<div class="lt-notif-item-body">
<div class="lt-notif-item-title">Export CSV complete — 42 rows</div>
<div class="lt-notif-item-time">1 hr ago</div>
</div>
</div>
<div class="lt-notif-item" role="button" tabindex="0" aria-label="Scheduled maintenance completed, 3 hr ago. Press Enter to dismiss.">
<span class="lt-notif-dot lt-notif-dot--read" aria-hidden="true"></span>
<div class="lt-notif-item-body">
<div class="lt-notif-item-title">Scheduled maintenance completed</div>
<div class="lt-notif-item-time">3 hr ago</div>
</div>
</div>
</div>
<div class="lt-notif-panel-footer">
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost" style="width:100%;font-size:0.72rem">View all notifications</button>
</div>
</div>
</div>
<!-- Current user -->
<span class="lt-header-user">operator</span>
<!-- Admin badge (only show for admins) -->
<span class="lt-badge-admin">admin</span>
</div>
</header>
<!-- Right-side detail drawer -->
<div id="lt-detail-drawer" class="lt-drawer-right" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Detail panel" data-overlay="lt-detail-overlay">
<div class="lt-drawer-right-header">
<span class="lt-drawer-right-title">// TICKET DETAIL</span>
<button type="button" class="lt-drawer-right-close" data-drawer-close aria-label="Close detail panel"></button>
</div>
<div class="lt-drawer-right-body">
<!-- Read-only meta row -->
<div class="lt-kv-grid" style="margin-bottom:1rem">
<div class="lt-kv-row"><span class="lt-kv-label">ID</span><span class="lt-kv-value lt-text-cyan">#123456789</span></div>
<div class="lt-kv-row"><span class="lt-kv-label">Created</span><span class="lt-kv-value lt-text-muted">2026-03-10 09:14 UTC</span></div>
</div>
<!-- Editable fields -->
<div class="lt-form-group">
<label class="lt-label" for="td-title">Title</label>
<input id="td-title" class="lt-input" type="text" value="Storage array link-down on compute-storage-01">
</div>
<div class="lt-drawer-form-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem">
<div class="lt-form-group">
<label class="lt-label" for="td-status">Status</label>
<select id="td-status" class="lt-select">
<option selected>Open</option>
<option>In Progress</option>
<option>Pending</option>
<option>Closed</option>
</select>
</div>
<div class="lt-form-group">
<label class="lt-label" for="td-priority">Priority</label>
<select id="td-priority" class="lt-select">
<option selected>P1 Critical</option>
<option>P2 High</option>
<option>P3 Medium</option>
<option>P4 Low</option>
</select>
</div>
</div>
<div class="lt-form-group">
<label class="lt-label" for="td-assignee">Assignee</label>
<select id="td-assignee" class="lt-select">
<option>Unassigned</option>
<option selected>jdoe</option>
<option>operator</option>
<option>admin</option>
</select>
</div>
<div class="lt-form-group">
<label class="lt-label" for="td-desc">Description</label>
<textarea id="td-desc" class="lt-input lt-textarea" rows="3" style="resize:vertical">Storage array link-down on compute-storage-01. Affects prod write path. Investigate RAID controller firmware.</textarea>
</div>
<!-- Add a comment -->
<div class="lt-divider-label" style="margin:1rem 0 0.75rem">Add Comment</div>
<div class="lt-form-group" style="margin-bottom:0.5rem">
<textarea id="td-comment" class="lt-input lt-textarea" rows="2" placeholder="Leave a comment…" style="resize:vertical" aria-label="Add comment"></textarea>
</div>
<button type="button" class="lt-btn lt-btn-sm" onclick="
const c=document.getElementById('td-comment');
if(c.value.trim()){lt.toast.success('Comment posted');c.value='';}
else lt.toast.warning('Comment is empty');
">Post Comment</button>
<!-- Activity timeline -->
<div class="lt-divider-label" style="margin:1rem 0 0.75rem">Activity</div>
<div class="lt-timeline">
<div class="lt-timeline-item lt-timeline-item--orange">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">jdoe</span> assigned ticket<span class="lt-timeline-time">2h ago</span></div>
<div class="lt-timeline-body">Assigned to self, escalated to P1.</div>
</div>
<div class="lt-timeline-item">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">sysbot</span> auto-created<span class="lt-timeline-time">3h ago</span></div>
<div class="lt-timeline-body">Alert triggered: <code>node_network_up = 0</code></div>
</div>
<div class="lt-timeline-item lt-timeline-item--dim">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">monitor</span> detected<span class="lt-timeline-time">3h ago</span></div>
<div class="lt-timeline-body">NIC link-down detected on large1:enp35s0.</div>
</div>
</div>
</div>
<div class="lt-drawer-right-footer">
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.rightDrawer.close('lt-detail-drawer')">Cancel</button>
<button type="button" class="lt-btn lt-btn-primary lt-btn-sm" onclick="lt.toast.success('Ticket #123456789 updated')">Save Changes</button>
</div>
</div>
<div id="lt-detail-overlay" class="lt-drawer-right-overlay"></div>
<!-- ===========================================================
MAIN CONTENT AREA
=========================================================== -->
<main class="lt-main lt-container" id="main-content">
<!-- Page title bar -->
<div class="lt-page-header">
<h1 class="lt-page-title">Dashboard</h1>
<div class="lt-btn-group">
<a href="/create" class="lt-btn lt-btn-primary">New Ticket</a>
<button type="button" class="lt-btn lt-btn-sm" data-modal-open="export-modal">Export</button>
</div>
</div>
<!-- ==========================================================
STATS WIDGETS
data-filter-key / data-filter-val → wired by lt.statsFilter
========================================================== -->
<div class="lt-stats-grid">
<div class="lt-stat-card active" role="button" tabindex="0" data-filter-key="status" data-filter-val="Open" aria-label="Open tickets: 42">
<span class="lt-stat-icon" aria-hidden="true">📋</span>
<div class="lt-stat-info">
<span class="lt-stat-value">42</span>
<span class="lt-stat-label">Open</span>
</div>
</div>
<div class="lt-stat-card" role="button" tabindex="0" data-filter-key="priority" data-filter-val="1" aria-label="Critical tickets: 3">
<span class="lt-stat-icon" aria-hidden="true">🔴</span>
<div class="lt-stat-info">
<span class="lt-stat-value">3</span>
<span class="lt-stat-label">Critical</span>
</div>
</div>
<div class="lt-stat-card" role="button" tabindex="0" data-filter-key="assigned_to" data-filter-val="0" aria-label="Unassigned tickets: 11">
<span class="lt-stat-icon" aria-hidden="true">👤</span>
<div class="lt-stat-info">
<span class="lt-stat-value">11</span>
<span class="lt-stat-label">Unassigned</span>
</div>
</div>
<div class="lt-stat-card" role="button" tabindex="0" data-filter-key="created" data-filter-val="today" aria-label="Today's tickets: 7">
<span class="lt-stat-icon" aria-hidden="true">📅</span>
<div class="lt-stat-info">
<span class="lt-stat-value">7</span>
<span class="lt-stat-label">Today</span>
</div>
</div>
</div>
<!-- ==========================================================
TAB NAVIGATION
========================================================== -->
<div class="lt-tabs" role="tablist" aria-label="Main views">
<button type="button" class="lt-tab active" role="tab" data-tab="tab-table" aria-selected="true" aria-controls="tab-table" id="tab-btn-table">Table View</button>
<button type="button" class="lt-tab" role="tab" data-tab="tab-kanban" aria-selected="false" aria-controls="tab-kanban" id="tab-btn-kanban">Kanban</button>
<button type="button" class="lt-tab" role="tab" data-tab="tab-workers" aria-selected="false" aria-controls="tab-workers" id="tab-btn-workers">Workers</button>
</div>
<!-- ==========================================================
SIDEBAR + CONTENT LAYOUT
========================================================== -->
<div class="lt-layout">
<!-- Sidebar filter panel -->
<aside class="lt-sidebar" id="lt-sidebar">
<div class="lt-sidebar-header">
Filters
<button type="button" class="lt-sidebar-toggle" data-sidebar-toggle="lt-sidebar"
aria-label="Collapse filters"></button>
</div>
<div class="lt-sidebar-body">
<fieldset class="lt-filter-group">
<legend class="lt-filter-label">Status</legend>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox" checked> Open
</label>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox"> Pending
</label>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox"> In Progress
</label>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox"> Closed
</label>
</fieldset>
<fieldset class="lt-filter-group">
<legend class="lt-filter-label">Priority</legend>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox"> P1 Critical
</label>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox"> P2 High
</label>
<label class="lt-filter-option">
<input type="checkbox" class="lt-checkbox"> P3 Medium
</label>
</fieldset>
<div class="lt-filter-group">
<label class="lt-filter-label" for="filter-assigned-to">Assigned To</label>
<div class="lt-form-group">
<select class="lt-select lt-btn-sm" id="filter-assigned-to">
<option value="">All users</option>
<option>operator</option>
<option>admin</option>
</select>
</div>
</div>
<div class="lt-btn-group">
<button type="button" class="lt-btn lt-btn-sm">Apply</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost">Reset</button>
</div>
</div>
</aside>
<!-- Main content -->
<div class="lt-content">
<!-- Toolbar: search + actions -->
<div class="lt-toolbar">
<div class="lt-toolbar-left">
<div class="lt-search">
<input type="search" class="lt-input lt-search-input" id="ticket-search"
placeholder="Search tickets..." aria-label="Search" autocomplete="off">
</div>
<!-- Advanced filter dropdown -->
<div class="lt-dropdown-wrap" id="adv-filter-wrap">
<button type="button" class="lt-btn lt-btn-sm lt-dropdown-trigger" id="adv-filter-btn" aria-expanded="false" aria-haspopup="true">Advanced ▾</button>
<div class="lt-dropdown-panel" id="adv-filter-panel" aria-hidden="true">
<div style="padding:0.75rem;display:grid;gap:0.5rem;width:clamp(200px,60vw,260px)">
<div class="lt-form-group" style="margin:0">
<label class="lt-label" style="font-size:0.75rem" for="adv-filter-status">Status</label>
<select id="adv-filter-status" class="lt-select" style="font-size:0.8rem">
<option value="">All</option>
<option>Open</option>
<option>In Progress</option>
<option>Pending</option>
<option>Closed</option>
</select>
</div>
<div class="lt-form-group" style="margin:0">
<label class="lt-label" style="font-size:0.75rem" for="adv-filter-priority">Priority</label>
<select id="adv-filter-priority" class="lt-select" style="font-size:0.8rem">
<option value="">All</option>
<option>P1 Critical</option>
<option>P2 High</option>
<option>P3 Medium</option>
<option>P4 Low</option>
</select>
</div>
<div class="lt-form-group" style="margin:0">
<label class="lt-label" style="font-size:0.75rem" for="adv-filter-assignee">Assignee</label>
<select id="adv-filter-assignee" class="lt-select" style="font-size:0.8rem">
<option value="">Anyone</option>
<option>Unassigned</option>
<option>jdoe</option>
<option>operator</option>
<option>admin</option>
</select>
</div>
<div style="display:flex;gap:0.5rem;margin-top:0.25rem">
<button type="button" class="lt-btn lt-btn-sm lt-btn-primary" style="flex:1" onclick="lt.toast.info('Filters applied');document.getElementById('adv-filter-panel').setAttribute('aria-hidden','true');document.getElementById('adv-filter-btn').setAttribute('aria-expanded','false')">Apply</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost" onclick="lt.toast.info('Filters cleared')">Reset</button>
</div>
</div>
</div>
</div>
</div>
<div class="lt-toolbar-right">
<span class="lt-text-muted lt-text-xs lt-hide-xs" id="ticket-result-count">42 results</span>
<!-- Bulk actions dropdown -->
<div class="lt-dropdown-wrap" id="bulk-action-wrap">
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost lt-dropdown-trigger" id="bulk-action-btn" aria-expanded="false" aria-haspopup="true">Bulk Actions ▾</button>
<div class="lt-dropdown-panel lt-dropdown-panel--right" id="bulk-action-panel" aria-hidden="true">
<button type="button" class="lt-dropdown-item" onclick="lt.toast.success('Closed selected tickets');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')">✓ Close Selected</button>
<button type="button" class="lt-dropdown-item" onclick="lt.toast.info('Reassign dialog…');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')">↩ Reassign…</button>
<button type="button" class="lt-dropdown-item" onclick="lt.toast.info('Exporting selected…');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')">⤓ Export Selected</button>
<div class="lt-dropdown-divider"></div>
<button type="button" class="lt-dropdown-item lt-dropdown-item--danger" onclick="lt.toast.error('Deleted selected');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')">🗑 Delete Selected</button>
</div>
</div>
</div>
</div>
<!-- ==================================================
TAB PANEL: TABLE VIEW
================================================== -->
<div id="tab-table" class="lt-tab-panel active" role="tabpanel" aria-labelledby="tab-btn-table">
<!-- Outer ASCII frame wrapping the table -->
<div class="lt-frame">
<span class="lt-frame-bl"></span>
<span class="lt-frame-br"></span>
<div class="lt-section-header">Ticket Queue</div>
<div class="lt-table-wrap">
<table class="lt-table lt-table-responsive" id="ticket-table" aria-label="Ticket queue">
<caption class="lt-sr-only">Ticket queue — sorted by priority</caption>
<thead>
<tr>
<th scope="col"><input type="checkbox" class="lt-checkbox" aria-label="Select all"></th>
<th scope="col" data-sort-key="id" aria-sort="none">ID</th>
<th scope="col" data-sort-key="priority" aria-sort="none">Priority</th>
<th scope="col" data-sort-key="title" aria-sort="none">Title</th>
<th scope="col" data-sort-key="status" aria-sort="none">Status</th>
<th scope="col" data-sort-key="assignee" aria-sort="none">Assignee</th>
<th scope="col" data-sort-key="created" aria-sort="none">Created</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<!-- P1 Critical row -->
<tr class="lt-row-p1 lt-row-critical">
<td data-label="Select"><input type="checkbox" class="lt-checkbox" aria-label="Select ticket #123456789"></td>
<td data-label="ID"><a href="/ticket/123456789">#123456789</a></td>
<td data-label="Priority"><span class="lt-p1">P1 Critical</span></td>
<td data-label="Title">Storage array link-down on compute-storage-01</td>
<td data-label="Status"><span class="lt-status lt-status-open">Open</span></td>
<td data-label="Assignee" class="lt-text-muted">Unassigned</td>
<td data-label="Created" class="lt-text-xs lt-text-muted">5m ago</td>
<td data-label="Actions">
<div class="lt-btn-group">
<a href="/ticket/123456789" class="lt-btn lt-btn-sm">View</a>
<button type="button" class="lt-btn lt-btn-sm lt-btn-danger">Close</button>
</div>
</td>
</tr>
<!-- P2 High row -->
<tr class="lt-row-p2 lt-row-warning">
<td data-label="Select"><input type="checkbox" class="lt-checkbox" aria-label="Select ticket #987654321"></td>
<td data-label="ID"><a href="/ticket/987654321">#987654321</a></td>
<td data-label="Priority"><span class="lt-p2">P2 High</span></td>
<td data-label="Title">Switch port flapping on USW-Pro-24</td>
<td data-label="Status"><span class="lt-status lt-status-in-progress">In Progress</span></td>
<td data-label="Assignee">operator</td>
<td data-label="Created" class="lt-text-xs lt-text-muted">2h ago</td>
<td data-label="Actions">
<div class="lt-btn-group">
<a href="/ticket/987654321" class="lt-btn lt-btn-sm">View</a>
</div>
</td>
</tr>
<!-- P3 Medium row -->
<tr class="lt-row-p3">
<td data-label="Select"><input type="checkbox" class="lt-checkbox" aria-label="Select ticket #111222333"></td>
<td data-label="ID"><a href="/ticket/111222333">#111222333</a></td>
<td data-label="Priority"><span class="lt-p3">P3 Med</span></td>
<td data-label="Title">Scheduled maintenance: replace SFP+ on large1</td>
<td data-label="Status"><span class="lt-status lt-status-pending">Pending</span></td>
<td data-label="Assignee">admin</td>
<td data-label="Created" class="lt-text-xs lt-text-muted">1d ago</td>
<td data-label="Actions">
<div class="lt-btn-group">
<a href="/ticket/111222333" class="lt-btn lt-btn-sm">View</a>
</div>
</td>
</tr>
<!-- P4 closed -->
<tr class="lt-row-p4">
<td data-label="Select"><input type="checkbox" class="lt-checkbox" aria-label="Select ticket #444555666"></td>
<td data-label="ID"><a href="/ticket/444555666">#444555666</a></td>
<td data-label="Priority"><span class="lt-p4">P4 Low</span></td>
<td data-label="Title">Update SSL cert on wiki.lotusguild.org</td>
<td data-label="Status"><span class="lt-status lt-status-closed">Closed</span></td>
<td data-label="Assignee">operator</td>
<td data-label="Created" class="lt-text-xs lt-text-muted">3d ago</td>
<td data-label="Actions">
<div class="lt-btn-group">
<a href="/ticket/444555666" class="lt-btn lt-btn-sm">View</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div><!-- /.lt-frame -->
</div><!-- /#tab-table -->
<!-- ==================================================
TAB PANEL: KANBAN
================================================== -->
<div id="tab-kanban" class="lt-tab-panel" role="tabpanel" aria-labelledby="tab-btn-kanban">
<div class="lt-grid-4">
<!-- Kanban column: Open -->
<div class="lt-frame">
<span class="lt-frame-bl"></span>
<span class="lt-frame-br"></span>
<div class="lt-section-header">Open</div>
<div class="lt-section-body" id="kanban-col-open" style="min-height:60px">
<div class="lt-card lt-mb-md lt-row-p1" role="article" aria-label="P1 — Storage array link-down, 5m ago, Unassigned">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p1" aria-hidden="true">P1</span>
<span class="lt-dot lt-dot-up" aria-hidden="true"></span>
</div>
<div class="lt-text-sm">Storage array link-down</div>
<div class="lt-text-xs lt-text-muted lt-mt-sm">5m ago · Unassigned</div>
</div>
<div class="lt-card lt-row-p3" role="article" aria-label="P3 — Update node_exporter on micro1, 1d ago, operator">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p3" aria-hidden="true">P3</span>
<span class="lt-dot lt-dot-up" aria-hidden="true"></span>
</div>
<div class="lt-text-sm">Update node_exporter on micro1</div>
<div class="lt-text-xs lt-text-muted lt-mt-sm">1d ago · operator</div>
</div>
</div>
</div>
<!-- Kanban column: Pending -->
<div class="lt-frame">
<span class="lt-frame-bl"></span>
<span class="lt-frame-br"></span>
<div class="lt-section-header">Pending</div>
<div class="lt-section-body" id="kanban-col-pending" style="min-height:60px">
<div class="lt-card lt-row-p2" role="article" aria-label="P2 — Scheduled maintenance window, 2d ago, admin">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p2" aria-hidden="true">P2</span>
<span class="lt-dot lt-dot-warn" aria-hidden="true"></span>
</div>
<div class="lt-text-sm">Scheduled maintenance window</div>
<div class="lt-text-xs lt-text-muted lt-mt-sm">2d ago · admin</div>
</div>
</div>
</div>
<!-- Kanban column: In Progress -->
<div class="lt-frame">
<span class="lt-frame-bl"></span>
<span class="lt-frame-br"></span>
<div class="lt-section-header">In Progress</div>
<div class="lt-section-body" id="kanban-col-inprogress" style="min-height:60px">
<div class="lt-card lt-row-p2 lt-item-running" role="article" aria-label="P2 — Switch port flapping on USW-Pro, 2h ago, operator">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p2" aria-hidden="true">P2</span>
<span class="lt-dot lt-dot-warn" aria-hidden="true"></span>
</div>
<div class="lt-text-sm">Switch port flapping on USW-Pro</div>
<div class="lt-text-xs lt-text-muted lt-mt-sm">2h ago · operator</div>
</div>
</div>
</div>
<!-- Kanban column: Closed -->
<div class="lt-frame">
<span class="lt-frame-bl"></span>
<span class="lt-frame-br"></span>
<div class="lt-section-header">Closed</div>
<div class="lt-section-body" id="kanban-col-closed" style="min-height:60px">
<div class="lt-card lt-row-p4" style="opacity:0.6" role="article" aria-label="P4 — Update SSL cert on wiki, 3d ago, operator">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p4" aria-hidden="true">P4</span>
<span class="lt-dot lt-dot-idle" aria-hidden="true"></span>
</div>
<div class="lt-text-sm">Update SSL cert on wiki</div>
<div class="lt-text-xs lt-text-muted lt-mt-sm">3d ago · operator</div>
</div>
</div>
</div>
</div><!-- /.lt-grid-4 -->
</div><!-- /#tab-kanban -->
<!-- ==================================================
TAB PANEL: WORKERS
================================================== -->
<div id="tab-workers" class="lt-tab-panel" role="tabpanel" aria-labelledby="tab-btn-workers">
<div class="lt-grid-3">
<div class="lt-card">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-text-amber lt-text-upper">pulse-worker-01</span>
<span class="lt-status lt-status-online">Online</span>
</div>
<div class="lt-data-table-wrapper">
<table class="lt-data-table" aria-label="pulse-worker-01 metrics">
<tbody>
<tr><th scope="row" class="lt-text-muted">CPU</th> <td>12%</td></tr>
<tr><th scope="row" class="lt-text-muted">Memory</th> <td>2.1 GB / 8 GB</td></tr>
<tr><th scope="row" class="lt-text-muted">Load</th> <td>0.42 / 0.51 / 0.48</td></tr>
<tr><th scope="row" class="lt-text-muted">Uptime</th> <td>14d 6h</td></tr>
<tr><th scope="row" class="lt-text-muted">Tasks</th> <td>2 / 5</td></tr>
</tbody>
</table>
</div>
</div>
<div class="lt-card">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-text-amber lt-text-upper">pulse-worker-02</span>
<span class="lt-status lt-status-offline">Offline</span>
</div>
<div class="lt-msg lt-msg-warning">Last seen 14m ago</div>
</div>
<div class="lt-card">
<div class="lt-empty">No more workers registered.</div>
</div>
</div>
</div><!-- /#tab-workers -->
</div><!-- /.lt-content -->
</div><!-- /.lt-layout -->
<!-- ==========================================================
COMPONENT SHOWCASE (remove in production)
========================================================== -->
<div class="lt-divider"></div>
<!-- Inner frame + subsection example -->
<div class="lt-frame">
<span class="lt-frame-bl"></span>
<span class="lt-frame-br"></span>
<div class="lt-section-header">Component Reference</div>
<div class="lt-frame-inner">
<div class="lt-subsection-header">Buttons</div>
<div class="lt-section-body">
<div class="lt-btn-group">
<button type="button" class="lt-btn">Default</button>
<button type="button" class="lt-btn lt-btn-primary">Primary</button>
<button type="button" class="lt-btn lt-btn-danger">Danger</button>
<button type="button" class="lt-btn lt-btn-sm">Small</button>
<button type="button" class="lt-btn lt-btn-ghost">Ghost</button>
<button type="button" class="lt-btn" disabled>Disabled</button>
</div>
</div>
<div class="lt-subsection-header">Status Badges</div>
<div class="lt-section-body lt-flex lt-flex-wrap lt-gap-md">
<span class="lt-status lt-status-open">Open</span>
<span class="lt-status lt-status-pending">Pending</span>
<span class="lt-status lt-status-in-progress">In Progress</span>
<span class="lt-status lt-status-closed">Closed</span>
<span class="lt-status lt-status-online">Online</span>
<span class="lt-status lt-status-offline">Offline</span>
<span class="lt-status lt-status-running">Running</span>
<span class="lt-status lt-status-completed">Completed</span>
<span class="lt-status lt-status-failed">Failed</span>
</div>
<div class="lt-subsection-header">Priority Badges</div>
<div class="lt-section-body lt-flex lt-flex-wrap lt-gap-md">
<span class="lt-priority lt-p1">P1 Critical</span>
<span class="lt-priority lt-p2">P2 High</span>
<span class="lt-priority lt-p3">P3 Med</span>
<span class="lt-priority lt-p4">P4 Low</span>
<span class="lt-priority lt-p5">P5 Min</span>
</div>
<div class="lt-subsection-header">Chips &amp; Badges</div>
<div class="lt-section-body lt-flex lt-flex-wrap lt-gap-sm">
<span class="lt-chip lt-chip-ok">OK</span>
<span class="lt-chip lt-chip-warn">Warning</span>
<span class="lt-chip lt-chip-critical">Critical</span>
<span class="lt-chip lt-chip-info">Info</span>
<span class="lt-badge lt-badge-green">v1.0</span>
<span class="lt-badge lt-badge-amber">Beta</span>
<span class="lt-badge lt-badge-red">Deprecated</span>
<span class="lt-badge-admin">admin</span>
</div>
<div class="lt-subsection-header">Status Dots</div>
<div class="lt-section-body lt-flex lt-gap-md">
<span class="lt-flex lt-gap-sm"><span class="lt-dot lt-dot-up"></span> Up</span>
<span class="lt-flex lt-gap-sm"><span class="lt-dot lt-dot-down"></span> Down</span>
<span class="lt-flex lt-gap-sm"><span class="lt-dot lt-dot-warn"></span> Degraded</span>
<span class="lt-flex lt-gap-sm"><span class="lt-dot lt-dot-idle"></span> Idle</span>
</div>
<div class="lt-subsection-header">Inline Messages</div>
<div class="lt-section-body">
<div class="lt-msg lt-msg-error">Database connection refused on 10.10.10.50</div>
<div class="lt-msg lt-msg-success">Ticket #123456789 updated successfully</div>
<div class="lt-msg lt-msg-warning">Rate limit: 80% consumed (40/50 req/min)</div>
<div class="lt-msg lt-msg-info">Auto-refresh active — updates every 30s</div>
</div>
<div class="lt-subsection-header">Forms</div>
<div class="lt-section-body">
<div class="lt-grid-2">
<div>
<div class="lt-form-group">
<label class="lt-label lt-label-required" for="eg-title">Ticket Title</label>
<input id="eg-title" type="text" class="lt-input" placeholder="Describe the issue">
</div>
<div class="lt-form-group">
<label class="lt-label" for="eg-priority">Priority</label>
<select id="eg-priority" class="lt-select">
<option>P1 — Critical</option>
<option>P2 — High</option>
<option selected>P3 — Medium</option>
<option>P4 — Low</option>
<option>P5 — Minimal</option>
</select>
</div>
<div class="lt-form-group">
<label class="lt-label" for="eg-desc">Description</label>
<textarea id="eg-desc" class="lt-textarea" placeholder="Markdown supported..."></textarea>
<span class="lt-form-hint">Markdown supported. Use #123456789 to link tickets.</span>
</div>
<div class="lt-form-group">
<label class="lt-label">
<input type="checkbox" class="lt-checkbox"> Confidential ticket
</label>
</div>
<div class="lt-btn-group">
<button type="button" class="lt-btn lt-btn-primary">Submit</button>
<button type="button" class="lt-btn lt-btn-ghost">Cancel</button>
</div>
</div>
<div>
<div class="lt-search lt-form-group">
<label class="lt-label" for="eg-search">Search</label>
<input id="eg-search" type="search" class="lt-input lt-search-input"
placeholder="Ctrl+K to focus" autocomplete="off">
</div>
<div class="lt-form-group">
<label class="lt-label">Loading state (skeleton)</label>
<div class="lt-skeleton lt-p-md" style="height:40px"></div>
</div>
</div>
<div class="lt-form-group">
<label class="lt-label">Empty state</label>
<div class="lt-empty" style="padding:1rem">No results found</div>
</div>
<div class="lt-form-group">
<label class="lt-label">Loading indicator</label>
<div class="lt-loading" style="padding:1rem"></div>
</div>
<div class="lt-form-group">
<label class="lt-label">Display-only fields <code style="font-size:0.7rem">.lt-display-field</code></label>
<select class="lt-select lt-display-field" style="margin-bottom:0.5rem">
<option>P3 — Medium</option>
</select>
<input type="text" class="lt-input lt-display-field" value="Read-only value">
</div>
</div>
</div>
</div>
<div class="lt-subsection-header">Log / Timeline Entries</div>
<div class="lt-section-body">
<div class="lt-log-entry success">
<div class="lt-log-ts">2026-03-14 09:12:33 EDT</div>
<strong>Status changed:</strong> Open → In Progress by operator
</div>
<div class="lt-log-entry warning">
<div class="lt-log-ts">2026-03-14 09:10:11 EDT</div>
<strong>Priority escalated:</strong> P3 → P1 by GANDALF auto-alert
</div>
<div class="lt-log-entry error">
<div class="lt-log-ts">2026-03-14 09:09:55 EDT</div>
<strong>Alert triggered:</strong> NIC link-down on large1:enp35s0
<div class="lt-log-output">node_exporter metric: node_network_up{interface="enp35s0"} = 0</div>
</div>
</div>
</div><!-- /.lt-frame-inner -->
</div><!-- /.lt-frame (component showcase) -->
<!-- ===========================================================
COMPONENT SHOWCASE — v1.2 ADDITIONS
=========================================================== -->
<div class="lt-frame lt-mt-lg">
<div class="lt-frame-inner">
<!-- PROGRESS BARS -->
<div class="lt-section-header">Progress Bars</div>
<div class="lt-section-body">
<div style="display:flex;flex-direction:column;gap:var(--space-md)">
<div>
<div class="lt-progress-label"><span>CPU LOAD</span><span>72%</span></div>
<div class="lt-progress" role="progressbar" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100" aria-label="CPU Load"><div class="lt-progress-bar" style="width:72%" data-width="72%"></div></div>
</div>
<div>
<div class="lt-progress-label"><span>MEMORY</span><span>45%</span></div>
<div class="lt-progress lt-progress--cyan" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" aria-label="Memory"><div class="lt-progress-bar" style="width:45%" data-width="45%"></div></div>
</div>
<div>
<div class="lt-progress-label"><span>DISK I/O</span><span>89%</span></div>
<div class="lt-progress lt-progress--red lt-progress--lg" role="progressbar" aria-valuenow="89" aria-valuemin="0" aria-valuemax="100" aria-label="Disk I/O"><div class="lt-progress-bar" style="width:89%" data-width="89%"></div></div>
</div>
<div>
<div class="lt-progress-label"><span>UPTIME</span><span>100%</span></div>
<div class="lt-progress lt-progress--green lt-progress--striped" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-label="Uptime"><div class="lt-progress-bar" style="width:100%" data-width="100%"></div></div>
</div>
</div>
</div>
<!-- BREADCRUMBS & PAGINATION -->
<div class="lt-section-header">Breadcrumbs / Pagination</div>
<div class="lt-section-body" style="display:flex;flex-direction:column;gap:var(--space-md)">
<nav class="lt-breadcrumb" aria-label="breadcrumb">
<div class="lt-breadcrumb-item"><a href="#">ROOT</a></div>
<div class="lt-breadcrumb-sep"></div>
<div class="lt-breadcrumb-item"><a href="#">SYSTEMS</a></div>
<div class="lt-breadcrumb-sep"></div>
<div class="lt-breadcrumb-item"><a href="#">NETWORK</a></div>
<div class="lt-breadcrumb-sep"></div>
<div class="lt-breadcrumb-item active">NODE-07</div>
</nav>
<nav class="lt-pagination" id="demo-pagination" aria-label="pagination"></nav>
</div>
<!-- ACCORDION -->
<div class="lt-section-header">Accordion</div>
<div class="lt-section-body">
<div>
<div class="lt-accordion">
<button type="button" class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-1" data-accordion>
SYSTEM OVERVIEW
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg>
</button>
<div class="lt-accordion-body" id="acc-body-1"><div class="lt-accordion-content">Node running at 72% CPU. 12 active processes. Last restart: 3d ago.</div></div>
</div>
<div class="lt-accordion">
<button type="button" class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-2" data-accordion>
NETWORK CONFIG
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg>
</button>
<div class="lt-accordion-body" id="acc-body-2"><div class="lt-accordion-content">eth0: 10.0.0.7 — MTU 1500 — RX 4.2 GB — TX 1.1 GB</div></div>
</div>
<div class="lt-accordion">
<button type="button" class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-3" data-accordion>
FIREWALL RULES
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg>
</button>
<div class="lt-accordion-body" id="acc-body-3"><div class="lt-accordion-content">22/tcp ALLOW — 80/tcp ALLOW — 443/tcp ALLOW — */* DENY</div></div>
</div>
</div>
</div>
<!-- ALERTS -->
<div class="lt-section-header">Alert Banners</div>
<div class="lt-section-body" style="display:flex;flex-direction:column;gap:var(--space-sm)">
<div class="lt-alert">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Information</div><div class="lt-alert-msg">Scheduled maintenance window: Sunday 02:0004:00 UTC.</div></div>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
<div class="lt-alert lt-alert--warning">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Warning</div><div class="lt-alert-msg">Disk usage on NODE-03 at 87%. Consider cleanup.</div></div>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
<div class="lt-alert lt-alert--error">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Critical</div><div class="lt-alert-msg">NODE-11 unreachable. Last seen 14m ago.</div></div>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
<div class="lt-alert lt-alert--success">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Success</div><div class="lt-alert-msg">Deployment v2.4.1 completed successfully.</div></div>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
</div>
<!-- SLA BANNERS -->
<div class="lt-section-header">SLA Banners</div>
<div class="lt-section-body" style="display:flex;flex-direction:column;gap:var(--space-sm)">
<div class="lt-sla-p1" role="alert" data-sla-id="demo-p1">
<span class="lt-sla-icon" aria-hidden="true">[ ! ]</span>
<div class="lt-sla-info">
<div class="lt-sla-title">P1 Critical — SLA: 6h 42m elapsed of 8h limit</div>
<div class="lt-sla-bar"><div class="lt-sla-fill" style="width:84%"></div></div>
</div>
<div class="lt-sla-meta">Storage array link-down · #123456789</div>
<button type="button" class="lt-sla-dismiss" aria-label="Dismiss"></button>
</div>
<div class="lt-sla-p2" role="alert" data-sla-id="demo-p2">
<span class="lt-sla-icon" aria-hidden="true">[ ~ ]</span>
<div class="lt-sla-info">
<div class="lt-sla-title">P2 High — SLA: 9h 37m elapsed of 24h limit</div>
<div class="lt-sla-bar"><div class="lt-sla-fill" style="width:40%"></div></div>
</div>
<div class="lt-sla-meta">Switch port flapping · #987654321</div>
<button type="button" class="lt-sla-dismiss" aria-label="Dismiss"></button>
</div>
</div>
<!-- TOGGLES, RANGE, TAGS -->
<div class="lt-section-header">Toggles / Range / Tags</div>
<div class="lt-section-body" style="display:flex;flex-direction:column;gap:var(--space-lg)">
<div style="display:flex;gap:var(--space-lg);flex-wrap:wrap">
<label class="lt-toggle">
<input type="checkbox" checked>
<div class="lt-toggle-track"><div class="lt-toggle-thumb"></div></div>
<span class="lt-toggle-label">Auto-refresh</span>
</label>
<label class="lt-toggle">
<input type="checkbox">
<div class="lt-toggle-track"><div class="lt-toggle-thumb"></div></div>
<span class="lt-toggle-label">Dark alerts</span>
</label>
<label class="lt-toggle">
<input type="checkbox" checked>
<div class="lt-toggle-track"><div class="lt-toggle-thumb"></div></div>
<span class="lt-toggle-label">Notifications</span>
</label>
</div>
<div class="lt-range-wrap" style="max-width:320px">
<div class="lt-range-header">
<span class="lt-range-label">Refresh Interval (s)</span>
<span class="lt-range-value">30</span>
</div>
<input type="range" class="lt-range" min="5" max="60" value="30" aria-label="Refresh Interval in seconds" aria-valuemin="5" aria-valuemax="60" aria-valuenow="30">
</div>
<div class="lt-tags">
<span class="lt-tag lt-tag--orange">CRITICAL</span>
<span class="lt-tag lt-tag--cyan">NETWORK</span>
<span class="lt-tag lt-tag--green">ONLINE</span>
<span class="lt-tag lt-tag--red">OFFLINE</span>
<span class="lt-tag lt-tag--purple">ADMIN</span>
<span class="lt-tag">UNTAGGED</span>
</div>
<div style="margin-top:var(--space-md);display:flex;flex-wrap:wrap;gap:var(--space-md);align-items:center">
<span class="lt-cursor" style="font-size:1.1rem;font-family:var(--font-mono)">SYSTEM STATUS</span>
<span class="lt-cursor lt-cursor--cyan" style="font-size:1.1rem;font-family:var(--font-mono)">SCANNING</span>
<span class="lt-cursor lt-cursor--orange" style="font-size:1.1rem;font-family:var(--font-mono)">AWAITING INPUT</span>
<span style="font-family:var(--font-crt);font-size:2rem;color:var(--accent-green)">42 NODES</span>
<span class="lt-tag lt-tag--red" style="font-family:var(--font-crt);font-size:1rem;letter-spacing:0.05em">CRITICAL</span>
</div>
</div>
<!-- CODE BLOCK -->
<div class="lt-section-header">Code Block</div>
<div class="lt-section-body">
<div class="lt-code-block">
<div class="lt-code-header">
<span class="lt-code-lang">bash</span>
<button type="button" class="lt-code-copy" data-copy="systemctl restart nginx && systemctl status nginx">COPY</button>
</div>
<pre><code><span class="tok-cmt"># Restart and verify nginx</span>
<span class="tok-kw">systemctl</span> restart nginx <span class="tok-kw">&amp;&amp;</span> <span class="tok-kw">systemctl</span> status nginx
<span class="tok-cmt"># Check active connections</span>
<span class="tok-kw">ss</span> -tlnp | <span class="tok-kw">grep</span> <span class="tok-str">':80\|:443'</span></code></pre>
</div>
</div>
<!-- KEY-VALUE + STEPPER -->
<div class="lt-section-header">Data Grid / Stepper</div>
<div class="lt-section-body" style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-lg)">
<div>
<div class="lt-kv-grid">
<div class="lt-kv-key">Hostname</div> <div class="lt-kv-val lt-kv-val--cyan">node-07.prod</div>
<div class="lt-kv-key">IP Address</div> <div class="lt-kv-val">10.0.0.7</div>
<div class="lt-kv-key">Status</div> <div class="lt-kv-val lt-kv-val--green">ONLINE</div>
<div class="lt-kv-key">Uptime</div> <div class="lt-kv-val">3d 14h 22m</div>
<div class="lt-kv-key">Load Avg</div> <div class="lt-kv-val lt-kv-val--orange">2.14 / 1.87 / 1.60</div>
<div class="lt-kv-key">Region</div> <div class="lt-kv-val">US-EAST-1</div>
</div>
</div>
<div>
<div class="lt-stepper">
<div class="lt-step complete">
<div class="lt-step-num"></div>
<div class="lt-step-label">INIT</div>
</div>
<div class="lt-step complete">
<div class="lt-step-num"></div>
<div class="lt-step-label">BUILD</div>
</div>
<div class="lt-step active">
<div class="lt-step-num">3</div>
<div class="lt-step-label">DEPLOY</div>
</div>
<div class="lt-step">
<div class="lt-step-num">4</div>
<div class="lt-step-label">VERIFY</div>
</div>
</div>
</div>
</div>
<!-- LIST GROUP + BADGES -->
<div class="lt-section-header">List Group / Badges</div>
<div class="lt-section-body" style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-lg)">
<div class="lt-list-group">
<div class="lt-list-item lt-list-item--active">
<span class="lt-dot lt-dot--green"></span>
<a href="#">node-01.prod</a>
<span class="lt-list-item-meta lt-kv-val--green">ONLINE</span>
</div>
<div class="lt-list-item">
<span class="lt-dot lt-dot--green"></span>
<a href="#">node-02.prod</a>
<span class="lt-list-item-meta lt-kv-val--green">ONLINE</span>
</div>
<div class="lt-list-item">
<span class="lt-dot lt-dot--orange"></span>
<a href="#">node-03.prod</a>
<span class="lt-list-item-meta lt-kv-val--orange">DEGRADED</span>
</div>
<div class="lt-list-item">
<span class="lt-dot lt-dot--red"></span>
<a href="#">node-11.prod</a>
<span class="lt-list-item-meta lt-kv-val--red">OFFLINE</span>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-md)">
<div style="display:flex;gap:var(--space-md);align-items:center;flex-wrap:wrap">
<div class="lt-badge-wrap">
<button type="button" class="lt-btn lt-btn-sm">Alerts</button>
<span class="lt-badge">3</span>
</div>
<div class="lt-badge-wrap">
<button type="button" class="lt-btn lt-btn-sm">Messages</button>
<span class="lt-badge lt-badge--orange">12</span>
</div>
<div class="lt-badge-wrap">
<button type="button" class="lt-btn lt-btn-sm">Updates</button>
<span class="lt-badge lt-badge--cyan">5</span>
</div>
</div>
<div style="display:flex;gap:var(--space-sm);align-items:center;flex-wrap:wrap">
<div class="lt-bar-loader"><span></span><span></span><span></span><span></span></div>
<div class="lt-spinner"></div>
<div class="lt-spinner lt-spinner--cyan lt-spinner--sm"></div>
<div class="lt-radar lt-radar--sm"></div>
<div class="lt-radar"></div>
<div class="lt-radar lt-radar--green"></div>
<div class="lt-pulse-dot"></div>
</div>
</div>
</div>
<!-- TAB BAR -->
<div class="lt-section-header">Tab Bar</div>
<div class="lt-section-body">
<div class="lt-tab-bar" role="tablist">
<button type="button" class="lt-tab active" role="tab" id="tab2-btn-overview" data-tab-target="tab-overview" aria-selected="true" aria-controls="tab-overview">Overview</button>
<button type="button" class="lt-tab" role="tab" id="tab2-btn-logs" data-tab-target="tab-logs" aria-selected="false" aria-controls="tab-logs">Logs</button>
<button type="button" class="lt-tab" role="tab" id="tab2-btn-metrics" data-tab-target="tab-metrics" aria-selected="false" aria-controls="tab-metrics">Metrics</button>
<button type="button" class="lt-tab" role="tab" id="tab2-btn-config" data-tab-target="tab-config" aria-selected="false" aria-controls="tab-config">Config</button>
</div>
<div class="lt-tab-panels" style="padding:var(--space-md) 0">
<div id="tab-overview" class="lt-tab-panel active" role="tabpanel" aria-labelledby="tab2-btn-overview">System overview: all nodes nominal. 14 active sessions.</div>
<div id="tab-logs" class="lt-tab-panel" role="tabpanel" aria-labelledby="tab2-btn-logs">
<pre style="font-family:var(--font-mono);font-size:0.75rem;color:var(--text-secondary);margin:0">[03:41:22] nginx: 200 GET /api/nodes (12ms)
[03:41:23] cron: heartbeat OK
[03:41:24] alert: disk 87% on node-03</pre>
</div>
<div id="tab-metrics" class="lt-tab-panel" role="tabpanel" aria-labelledby="tab2-btn-metrics">CPU avg: 34% — Mem avg: 61% — Net TX: 4.2 Mbps</div>
<div id="tab-config" class="lt-tab-panel" role="tabpanel" aria-labelledby="tab2-btn-config">Config last updated: 2026-03-20 by admin@lotusguild.io</div>
</div>
</div>
<!-- TOOLTIP & DIVIDER DEMO -->
<div class="lt-section-header">Tooltips / Divider</div>
<div class="lt-section-body" style="display:flex;flex-direction:column;gap:var(--space-md)">
<div style="display:flex;gap:var(--space-lg);flex-wrap:wrap;padding-top:var(--space-md)">
<span data-tooltip="Primary accent — use for actions" class="lt-tag lt-tag--orange">ORANGE</span>
<span data-tooltip="Info / border color" data-tooltip-pos="bottom" class="lt-tag lt-tag--cyan">CYAN (bottom)</span>
<span data-tooltip="Success states" class="lt-tag lt-tag--green">GREEN</span>
<span data-tooltip="Critical / error" class="lt-tag lt-tag--red">RED</span>
</div>
<div class="lt-divider"><span class="lt-divider-label">SECTION BREAK</span></div>
<div class="lt-divider lt-divider--orange"><span class="lt-divider-label">CRITICAL ZONE</span></div>
</div>
<!-- DROPZONE -->
<div class="lt-section-header">File Dropzone</div>
<div class="lt-section-body">
<div class="lt-dropzone" data-dropzone data-accept=".json,.csv,.log" data-max-size="10485760">
<input type="file" accept=".json,.csv,.log" multiple aria-label="Upload JSON, CSV, or log files (max 10 MB each)">
<div class="lt-dropzone-icon"></div>
<div class="lt-dropzone-text">Drop files here or <strong>click to browse</strong></div>
<div class="lt-dropzone-hint">Accepts .json, .csv, .log — max 10 MB each</div>
</div>
<div class="lt-file-list" id="demo-file-list"></div>
</div>
<!-- COMMAND PALETTE TRIGGER -->
<div class="lt-section-header">Command Palette</div>
<div class="lt-section-body">
<p style="font-family:var(--font-mono);font-size:0.8rem;color:var(--text-secondary);margin:0 0 var(--space-sm)">
Press <kbd style="background:var(--bg-tertiary);border:1px solid var(--border-dim);padding:2px 6px;font-family:var(--font-mono)">Ctrl+K</kbd> or click the button below to open the command palette.
</p>
<button type="button" class="lt-btn lt-btn-ghost" onclick="lt.cmdPalette.open()">⌘ Open Command Palette</button>
</div>
</div><!-- /.lt-frame-inner (v1.2 additions) -->
</div><!-- /.lt-frame (v1.2 additions) -->
<!-- ═══════════════════════════════════════════════════════════
v1.1 COMPONENT SHOWCASE
═══════════════════════════════════════════════════════════ -->
<!-- Theme Toggle -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// THEME TOGGLE</span>
</div>
<div class="lt-section-body">
<p style="font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem">Dark/light mode with OS preference detection and localStorage persistence.</p>
<div class="lt-flex lt-gap-sm lt-align-center lt-wrap">
<button type="button" class="lt-btn lt-btn-primary" onclick="lt.theme.toggle()">Toggle Theme</button>
<button type="button" class="lt-btn" onclick="lt.theme.set('dark')">Force Dark</button>
<button type="button" class="lt-btn" onclick="lt.theme.set('light')">Force Light</button>
<code style="font-size:0.72rem;color:var(--accent-cyan)">lt.theme.toggle() | .set('light') | .get()</code>
</div>
</div>
</div>
<!-- Skeleton Loaders -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// SKELETON LOADERS</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-3" style="gap:1rem;align-items:start">
<div class="lt-skeleton-card">
<div class="lt-skeleton-card-header">
<span class="lt-skeleton lt-skeleton-avatar"></span>
<div style="flex:1"><span class="lt-skeleton lt-skeleton-title"></span><span class="lt-skeleton lt-skeleton-line-sm"></span></div>
</div>
<span class="lt-skeleton lt-skeleton-text"></span>
<span class="lt-skeleton lt-skeleton-line-lg"></span>
<span class="lt-skeleton lt-skeleton-text" style="width:70%"></span>
<div class="lt-flex lt-gap-sm" style="margin-top:0.25rem">
<span class="lt-skeleton lt-skeleton-btn"></span>
<span class="lt-skeleton lt-skeleton-badge"></span>
</div>
</div>
<div style="grid-column:span 2">
<div class="lt-skeleton-row"><span class="lt-skeleton" style="height:1rem"></span><span class="lt-skeleton lt-skeleton-text"></span><span class="lt-skeleton" style="height:0.8rem;width:80%"></span><span class="lt-skeleton lt-skeleton-badge"></span><span class="lt-skeleton" style="height:0.8rem;width:60%"></span><span class="lt-skeleton lt-skeleton-btn"></span></div>
<div class="lt-skeleton-row" style="opacity:0.65"><span class="lt-skeleton" style="height:1rem"></span><span class="lt-skeleton lt-skeleton-text"></span><span class="lt-skeleton" style="height:0.8rem;width:65%"></span><span class="lt-skeleton lt-skeleton-badge"></span><span class="lt-skeleton" style="height:0.8rem;width:50%"></span><span class="lt-skeleton lt-skeleton-btn"></span></div>
<div class="lt-skeleton-row" style="opacity:0.35"><span class="lt-skeleton" style="height:1rem"></span><span class="lt-skeleton lt-skeleton-text"></span><span class="lt-skeleton" style="height:0.8rem;width:55%"></span><span class="lt-skeleton lt-skeleton-badge"></span><span class="lt-skeleton" style="height:0.8rem;width:40%"></span><span class="lt-skeleton lt-skeleton-btn"></span></div>
</div>
</div>
</div>
</div>
<!-- Empty States -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// EMPTY STATES</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-3" style="gap:1rem">
<div class="lt-card"><div class="lt-empty-state"><div class="lt-empty-state-icon">📭</div><div class="lt-empty-state-title">No Tickets Found</div><div class="lt-empty-state-body">No tickets match your current filters.</div><button type="button" class="lt-btn lt-btn-sm lt-btn-primary">Clear Filters</button></div></div>
<div class="lt-card"><div class="lt-empty-state"><div class="lt-empty-state-icon">🔌</div><div class="lt-empty-state-title">No Workers Online</div><div class="lt-empty-state-body">All workers are offline or unreachable.</div><button type="button" class="lt-btn lt-btn-sm" onclick="lt.toast.warning('Checking workers…')">Retry</button></div></div>
<div class="lt-card"><div class="lt-empty-state lt-empty-state--sm"><div class="lt-empty-state-icon">🗂</div><div class="lt-empty-state-title">No Results</div><div class="lt-empty-state-body">Try a different search term.</div></div></div>
</div>
</div>
</div>
<!-- Avatars & Notification Badges -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// AVATARS &amp; NOTIFICATION BADGES</span>
</div>
<div class="lt-section-body">
<div class="lt-flex lt-gap-lg lt-wrap lt-align-center" style="margin-bottom:1rem">
<span class="lt-avatar lt-avatar--xs lt-avatar--orange">AB</span>
<span class="lt-avatar lt-avatar--sm lt-avatar--green">CD</span>
<span class="lt-avatar lt-avatar--purple">EF</span>
<span class="lt-avatar lt-avatar--lg lt-avatar--red">GH</span>
<div class="lt-avatar-wrap"><span class="lt-avatar lt-avatar--orange">JD</span><span class="lt-avatar-status lt-avatar-status--online"></span></div>
<div class="lt-avatar-wrap"><span class="lt-avatar">SK</span><span class="lt-avatar-status lt-avatar-status--away"></span></div>
<div class="lt-avatar-wrap"><span class="lt-avatar lt-avatar--red">MR</span><span class="lt-avatar-status lt-avatar-status--busy"></span></div>
<div class="lt-avatar-group">
<span class="lt-avatar lt-avatar--sm lt-avatar--orange">AA</span>
<span class="lt-avatar lt-avatar--sm lt-avatar--green">BB</span>
<span class="lt-avatar lt-avatar--sm lt-avatar--purple">CC</span>
<span class="lt-avatar lt-avatar--sm" style="background:var(--bg-tertiary);color:var(--text-muted);font-size:0.6rem">+4</span>
</div>
</div>
<div class="lt-flex lt-gap-md lt-align-center lt-wrap">
<span class="lt-notif-wrap" id="demo-notif-btn"><button type="button" class="lt-btn lt-btn-sm">🔔 Alerts</button></span>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.notif.inc('#demo-notif-btn')">+1 Badge</button>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.notif.clear('#demo-notif-btn')">Clear</button>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.notif.inc('#lt-notif-bell')">+1 Header Bell</button>
</div>
</div>
</div>
<!-- Timeline -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// TIMELINE / ACTIVITY FEED</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-2" style="gap:1.5rem">
<div class="lt-timeline">
<div class="lt-timeline-item lt-timeline-item--red">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">alertmanager</span> fired<span class="lt-timeline-time lt-num">14:22:05</span></div>
<div class="lt-timeline-body">CRITICAL: Storage array link-down on <code>compute-storage-01</code>. Ticket auto-created.</div>
</div>
<div class="lt-timeline-item lt-timeline-item--orange">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">jdoe</span> assigned<span class="lt-timeline-time lt-num">14:24:11</span></div>
<div class="lt-timeline-body">Escalated to P1 Critical. Paged on-call team.</div>
</div>
<div class="lt-timeline-item">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">jdoe</span> commented<span class="lt-timeline-time lt-num">14:31:44</span></div>
<div class="lt-timeline-body">Confirmed NIC failure. Ordered replacement hardware. ETA 2h.</div>
</div>
<div class="lt-timeline-item lt-timeline-item--green">
<div class="lt-timeline-meta"><span class="lt-timeline-actor">jdoe</span> resolved<span class="lt-timeline-time lt-num">16:55:00</span></div>
<div class="lt-timeline-body">Hardware replaced. Link restored. Monitoring for 30 min.</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:0.75rem">
<div class="lt-stat-card"><div class="lt-stat-label">Resolution Time</div><div class="lt-stat-value lt-text-green lt-num">2h 33m</div></div>
<div class="lt-stat-card"><div class="lt-stat-label">Events</div><div class="lt-stat-value lt-num">4</div></div>
<div class="lt-stat-card"><div class="lt-stat-label">SLA Status</div><div class="lt-stat-value lt-text-orange">Within SLA</div></div>
</div>
</div>
</div>
</div>
<!-- Right Drawer -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// RIGHT-SIDE DRAWER</span>
</div>
<div class="lt-section-body">
<p style="font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem">Detail/inspect panel from the right. Focus trap, ESC close, overlay backdrop, return-focus.</p>
<div class="lt-flex lt-gap-sm lt-wrap">
<button type="button" class="lt-btn lt-btn-primary" data-drawer-open="lt-detail-drawer">Open Detail Panel</button>
<code style="font-size:0.72rem;color:var(--accent-cyan)">lt.rightDrawer.open('id') | .close() | .toggle()</code>
</div>
</div>
</div>
<!-- Context Menu -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// CONTEXT MENU</span>
</div>
<div class="lt-section-body">
<p style="font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem">Right-click any element with <code>data-context-menu</code> or trigger programmatically.</p>
<div class="lt-flex lt-gap-sm lt-wrap lt-align-center">
<div data-context-menu="demo-ctx" class="lt-card" style="padding:0.75rem 1.25rem;cursor:context-menu;border-style:dashed;display:inline-block">
Right-click this card
</div>
<button type="button" class="lt-btn lt-btn-sm" id="demo-ctx-btn">Show Context Menu</button>
</div>
</div>
</div>
<!-- Combobox + Typeahead -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// COMBOBOX &amp; TYPEAHEAD</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-2" style="gap:1.5rem">
<div>
<label class="lt-label" for="demo-combobox-input">Assign Workers (multi-select)</label>
<div class="lt-combobox" id="demo-combobox">
<div class="lt-combobox-input-wrap">
<input type="text" class="lt-combobox-input" id="demo-combobox-input" placeholder="Search workers…" autocomplete="off">
</div>
<div class="lt-combobox-dropdown"></div>
</div>
</div>
<div>
<label class="lt-label" for="demo-typeahead-input">Search Tickets (typeahead)</label>
<div class="lt-typeahead" id="demo-typeahead">
<input type="text" class="lt-input lt-w-full" id="demo-typeahead-input" placeholder="Type to search…" autocomplete="off">
<div class="lt-typeahead-dropdown"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Sticky Table -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// STICKY TABLE HEADERS</span>
</div>
<div class="lt-section-body">
<div class="lt-table-sticky-wrap">
<table class="lt-table lt-table-responsive">
<thead><tr><th scope="col">ID</th><th scope="col">Priority</th><th scope="col">Title</th><th scope="col">Status</th><th scope="col">Assignee</th></tr></thead>
<tbody>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#001</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p1">P1</span></td><td data-label="Title">Link-down on compute-storage-01</td><td data-label="Status"><span class="lt-badge lt-badge-open">Open</span></td><td data-label="Assignee">jdoe</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#002</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p2">P2</span></td><td data-label="Title">Switch port flapping USW-Pro-24</td><td data-label="Status"><span class="lt-badge lt-badge-in-progress">In Progress</span></td><td data-label="Assignee">smith</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#003</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p3">P3</span></td><td data-label="Title">Scheduled SFP+ replacement</td><td data-label="Status"><span class="lt-badge lt-badge-pending">Pending</span></td><td data-label="Assignee">ops-bot</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#004</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p4">P4</span></td><td data-label="Title">SSL cert renewal wiki</td><td data-label="Status"><span class="lt-badge lt-badge-open">Open</span></td><td data-label="Assignee">admin</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#005</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p1">P1</span></td><td data-label="Title">RAID controller firmware</td><td data-label="Status"><span class="lt-badge lt-badge-open">Open</span></td><td data-label="Assignee">jdoe</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#006</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p2">P2</span></td><td data-label="Title">Backup job failure nas-01</td><td data-label="Status"><span class="lt-badge lt-badge-closed">Closed</span></td><td data-label="Assignee">backup-bot</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#007</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p3">P3</span></td><td data-label="Title">Prometheus alert rule tuning</td><td data-label="Status"><span class="lt-badge lt-badge-in-progress">In Progress</span></td><td data-label="Assignee">ops-team</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Chart Containers -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// CHART CONTAINERS</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-2" style="gap:1rem;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))">
<div class="lt-chart-wrap">
<div class="lt-chart-header">
<span class="lt-chart-title">Ticket Volume (7d)</span>
<div class="lt-chart-legend">
<span class="lt-chart-legend-item"><span class="lt-chart-legend-dot" style="background:var(--accent-cyan)"></span>Open</span>
<span class="lt-chart-legend-item"><span class="lt-chart-legend-dot" style="background:var(--accent-green)"></span>Closed</span>
</div>
</div>
<div class="lt-chart-body" style="min-height:120px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:0.72rem;letter-spacing:0.1em">[ Plug in Chart.js / D3 here ]</div>
<div class="lt-chart-axis"><span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span><span>Sun</span></div>
</div>
<div class="lt-chart-wrap is-loading">
<div class="lt-chart-header"><span class="lt-chart-title">Worker Uptime</span></div>
<div class="lt-chart-body"></div>
</div>
</div>
</div>
</div>
<!-- Split Pane -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// SPLIT PANE</span>
</div>
<div class="lt-section-body">
<div class="lt-split" id="demo-split" style="height:clamp(160px,30vh,240px);border:1px solid var(--border-dim)">
<div class="lt-split-pane" style="padding:1rem;overflow:auto">
<div class="lt-section-label" style="margin-bottom:0.5rem">Panel A</div>
<p style="font-size:0.78rem;color:var(--text-secondary)">Drag the divider to resize. Stacks vertically on mobile.</p>
</div>
<div class="lt-split-divider" title="Drag to resize"></div>
<div class="lt-split-pane" style="padding:1rem;overflow:auto">
<div class="lt-section-label" style="margin-bottom:0.5rem">Panel B</div>
<p style="font-size:0.78rem;color:var(--text-secondary)">Both panels maintain independent scrolling.</p>
</div>
</div>
<p style="font-size:0.72rem;color:var(--text-muted);margin-top:0.5rem"><code>lt.splitPane.init(el, &#123; initial: 0.4, minA: 120 &#125;)</code></p>
</div>
</div>
<!-- WebSocket & Offline -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// WEBSOCKET &amp; OFFLINE DETECTION</span>
</div>
<div class="lt-section-body">
<div class="lt-flex lt-gap-md lt-wrap lt-align-center" style="margin-bottom:1rem">
<div class="lt-ws-status" data-state="connected"><span class="lt-dot"></span><span>Connected</span></div>
<div class="lt-ws-status" data-state="connecting"><span class="lt-dot"></span><span>Connecting…</span></div>
<div class="lt-ws-status" data-state="disconnected"><span class="lt-dot"></span><span>Disconnected</span></div>
</div>
<div class="lt-flex lt-gap-sm lt-wrap">
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.offline.isOnline() ? lt.toast.success('Online ✓') : lt.toast.error('Offline ✗')">Check Online Status</button>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.toast.info('lt.ws.connect(url, { reconnect:true, onMessage: fn })')">WS API Hint</button>
</div>
<p style="font-size:0.72rem;color:var(--text-muted);margin-top:0.75rem">Offline banner + body class auto-applied on <code>navigator.onLine</code> change. WS manager has exponential backoff, event emitter, status indicator binding.</p>
</div>
</div>
<!-- Wizard / Multi-Step Form -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// WIZARD / MULTI-STEP FORM</span>
</div>
<div class="lt-section-body">
<div id="demo-wizard">
<!-- Step indicators -->
<div class="lt-wizard-steps">
<div class="lt-wizard-step is-active" data-wizard-indicator aria-current="step">
<div class="lt-wizard-num">1</div>
<div class="lt-wizard-label">Details</div>
</div>
<div class="lt-wizard-step" data-wizard-indicator>
<div class="lt-wizard-num">2</div>
<div class="lt-wizard-label">Assign</div>
</div>
<div class="lt-wizard-step" data-wizard-indicator>
<div class="lt-wizard-num">3</div>
<div class="lt-wizard-label">Review</div>
</div>
</div>
<!-- Counter -->
<p class="lt-wizard-counter">Step <strong data-wizard-current>1</strong> of <strong data-wizard-total>3</strong></p>
<!-- Steps -->
<div data-wizard-step="1" class="is-active">
<div class="lt-grid lt-grid-2" style="gap:1rem;margin-top:1rem">
<div class="lt-form-group"><label class="lt-label" for="wizard-title">Ticket Title</label><input id="wizard-title" type="text" name="title" class="lt-input lt-w-full" placeholder="Brief description…"></div>
<div class="lt-form-group"><label class="lt-label" for="wizard-priority">Priority</label><select id="wizard-priority" name="priority" class="lt-select lt-w-full"><option value="p1">P1 Critical</option><option value="p2">P2 High</option><option value="p3" selected>P3 Medium</option><option value="p4">P4 Low</option></select></div>
</div>
</div>
<div data-wizard-step="2" aria-hidden="true">
<div class="lt-form-group" style="margin-top:1rem"><label class="lt-label" for="wizard-assignee">Assign To</label><input id="wizard-assignee" type="text" name="assignee" class="lt-input lt-w-full" placeholder="Username…"></div>
<div class="lt-form-group"><label class="lt-label" for="wizard-due">Due Date</label><input id="wizard-due" type="date" name="due" class="lt-input"></div>
</div>
<div data-wizard-step="3" aria-hidden="true">
<div class="lt-empty-state lt-empty-state--sm"><div class="lt-empty-state-icon"></div><div class="lt-empty-state-title">Review &amp; Submit</div><div class="lt-empty-state-body">Check the details above and click Submit when ready.</div></div>
</div>
<!-- Nav -->
<div class="lt-wizard-nav">
<button type="button" class="lt-btn lt-btn-sm" data-wizard-prev disabled>← Back</button>
<button type="button" class="lt-btn lt-btn-primary lt-btn-sm" data-wizard-next>Next →</button>
<button type="button" class="lt-btn lt-btn-primary lt-btn-sm" data-wizard-done style="display:none">Submit ✓</button>
</div>
</div>
</div>
</div>
<!-- Sortable List -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// DRAG-TO-REORDER (SORTABLE)</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-2" style="gap:1.5rem;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))">
<div>
<p style="font-size:0.75rem;color:var(--text-muted);margin-bottom:0.75rem">Drag items to reorder. Uses native HTML5 drag API with pointer-events fallback.</p>
<ul id="demo-sortable" style="list-style:none;padding:0;display:flex;flex-direction:column;gap:4px">
<li data-id="p1" style="padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem"><span style="color:var(--text-muted);cursor:grab"></span> P1 — Storage link-down</li>
<li data-id="p2" style="padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem"><span style="color:var(--text-muted);cursor:grab"></span> P2 — Switch port flapping</li>
<li data-id="p3" style="padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem"><span style="color:var(--text-muted);cursor:grab"></span> P3 — SFP+ replacement</li>
<li data-id="p4" style="padding:0.5rem 0.75rem;background:var(--bg-card);border:1px solid var(--border-dim);display:flex;align-items:center;gap:0.5rem"><span style="color:var(--text-muted);cursor:grab"></span> P4 — SSL cert renewal</li>
</ul>
<p id="demo-sort-order" style="font-size:0.68rem;color:var(--text-muted);margin-top:0.5rem">Order: p1, p2, p3, p4</p>
</div>
<div>
<p style="font-size:0.72rem;color:var(--text-muted);margin-bottom:0.75rem">API:</p>
<pre class="lt-code-block" style="font-size:0.7rem">const s = lt.sortable.init(listEl, {
onSort: (items) => console.log(s.getOrder())
});</pre>
</div>
</div>
</div>
</div>
<!-- Countdown / Timer -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// COUNTDOWN &amp; TIMER</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-3" style="gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))">
<div class="lt-stat-card">
<div class="lt-stat-label">SLA Countdown</div>
<div class="lt-countdown lt-num" id="demo-countdown">--:--:--</div>
<div style="font-size:0.68rem;color:var(--text-muted);margin-top:0.25rem">Goes urgent (red) at &lt;5 min</div>
</div>
<div class="lt-stat-card">
<div class="lt-stat-label">Stopwatch</div>
<div class="lt-countdown lt-num" id="demo-stopwatch">00:00:00</div>
<div class="lt-flex lt-gap-xs" style="margin-top:0.5rem">
<button type="button" class="lt-btn lt-btn-sm" id="sw-pause">Pause</button>
<button type="button" class="lt-btn lt-btn-sm" id="sw-reset">Reset</button>
</div>
</div>
<div class="lt-stat-card">
<div class="lt-stat-label">API</div>
<pre class="lt-code-block" style="font-size:0.62rem;margin:0">lt.timer.countdown(el, date, {
urgent: 300,
onExpire: () => {}
});
lt.timer.stopwatch(el);</pre>
</div>
</div>
</div>
</div>
<!-- Image Lightbox -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// IMAGE LIGHTBOX</span>
</div>
<div class="lt-section-body">
<p style="font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem">Click any image to open full-screen viewer. Keyboard: ←/→ navigate, Esc closes.</p>
<div class="lt-flex lt-gap-md lt-wrap">
<img class="lt-lightbox-demo" src="https://picsum.photos/seed/lt1/320/180" alt="Server rack overview" style="width:160px;height:90px;object-fit:cover;border:1px solid var(--border-dim)" loading="lazy">
<img class="lt-lightbox-demo" src="https://picsum.photos/seed/lt2/320/180" alt="Network switch closeup" style="width:160px;height:90px;object-fit:cover;border:1px solid var(--border-dim)" loading="lazy">
<img class="lt-lightbox-demo" src="https://picsum.photos/seed/lt3/320/180" alt="Datacenter floor view" style="width:160px;height:90px;object-fit:cover;border:1px solid var(--border-dim)" loading="lazy">
</div>
<p style="font-size:0.72rem;color:var(--text-muted);margin-top:0.5rem"><code>lt.lightbox.init('.lt-lightbox-demo', { caption: 'alt', loop: true })</code></p>
</div>
</div>
<!-- Sidebar Submenus -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// SIDEBAR SUBMENUS</span>
</div>
<div class="lt-section-body">
<div style="width:220px;background:var(--bg-secondary);border:1px solid var(--border-dim);padding:0.5rem">
<a href="#" class="lt-sidebar-link active" style="display:flex;align-items:center;gap:0.5rem;padding:0.4rem 0.75rem;font-size:0.78rem;color:var(--accent-orange);text-decoration:none">⊞ Dashboard</a>
<a href="#" class="lt-sidebar-link" style="display:flex;align-items:center;gap:0.5rem;padding:0.4rem 0.75rem;font-size:0.78rem;color:var(--text-secondary);text-decoration:none">🎫 Tickets</a>
<div class="lt-sidebar-group is-open">
<div class="lt-sidebar-group-label">Admin <span class="chevron"></span></div>
<div class="lt-sidebar-submenu">
<a href="#" class="lt-sidebar-sub-link active" aria-current="page">⚙ Templates</a>
<a href="#" class="lt-sidebar-sub-link">🔀 Workflow</a>
<a href="#" class="lt-sidebar-sub-link">📋 Audit Log</a>
<a href="#" class="lt-sidebar-sub-link">🔑 API Keys</a>
</div>
</div>
<div class="lt-sidebar-group">
<div class="lt-sidebar-group-label">Reports <span class="chevron"></span></div>
<div class="lt-sidebar-submenu">
<a href="#" class="lt-sidebar-sub-link">📊 SLA Report</a>
<a href="#" class="lt-sidebar-sub-link">📈 Volume Trends</a>
</div>
</div>
</div>
</div>
</div>
<!-- Markdown Renderer -->
<div class="lt-section">
<div class="lt-section-header">
<span class="lt-section-title">// MARKDOWN RENDERER</span>
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-2" style="gap:1.5rem">
<div>
<div class="lt-section-label" style="margin-bottom:0.5rem">Source</div>
<pre class="lt-code-block" style="font-size:0.7rem"># Incident Report
**Severity**: P1 Critical
## Summary
Storage array link-down on `compute-storage-01`.
- Affected: 3 production services
- Duration: 2h 33m
- Root cause: NIC hardware failure
> Resolved. Monitoring continues.
[View Ticket](#001)</pre>
</div>
<div>
<div class="lt-section-label" style="margin-bottom:0.5rem">Rendered</div>
<div id="demo-markdown" class="lt-markdown" style="background:var(--bg-card);border:1px solid var(--border-dim);padding:1rem" data-markdown="# Incident Report&#10;**Severity**: P1 Critical&#10;&#10;## Summary&#10;Storage array link-down on &#96;compute-storage-01&#96;.&#10;&#10;- Affected: 3 production services&#10;- Duration: 2h 33m&#10;- Root cause: NIC hardware failure&#10;&#10;&gt; Resolved. Monitoring continues.&#10;&#10;[View Ticket](#001)"></div>
</div>
</div>
<p style="font-size:0.72rem;color:var(--text-muted);margin-top:0.75rem">Built-in micro-renderer (no deps). Drops in <code>window.marked</code> or <code>window.markdownit</code> automatically if present.</p>
</div>
</div>
</main><!-- /.lt-main -->
<!-- ================================================================
FOOTER LANDMARK
================================================================ -->
<footer class="lt-footer" role="contentinfo" aria-label="Application footer">
<span class="lt-text-muted lt-font-mono lt-text-xs">
LotusGuild Terminal Design System v1.2 &mdash; Internal Use Only
</span>
<span class="lt-text-muted lt-font-mono lt-text-xs">
&copy; <span id="footer-year"></span> LotusGuild. All rights reserved.
</span>
</footer>
<!-- ===========================================================
TOAST DEMO BUTTONS (remove in production)
=========================================================== -->
<div style="position:fixed;bottom:1rem;left:1rem;display:flex;flex-direction:column;gap:0.5rem;z-index:900">
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.toast.success('Ticket saved successfully')">✓ Toast</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-danger" onclick="lt.toast.error('Network error — retry in 5s', 5000)">✗ Error</button>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.toast.warning('Rate limit 80% used')">! Warn</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost" onclick="lt.toast.info('Auto-refresh triggered')">i Info</button>
</div>
<!-- ===========================================================
MODAL EXAMPLE: Export
=========================================================== -->
<div id="export-modal" class="lt-modal-overlay" aria-hidden="true">
<div class="lt-modal" role="dialog" aria-modal="true" aria-labelledby="export-modal-title">
<div class="lt-modal-header">
<span class="lt-modal-title" id="export-modal-title">Export Tickets</span>
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close"></button>
</div>
<div class="lt-modal-body">
<div class="lt-form-group">
<label class="lt-label" for="export-fmt">Format</label>
<select id="export-fmt" class="lt-select">
<option>CSV</option>
<option>JSON</option>
</select>
</div>
<div class="lt-form-group">
<label class="lt-label">
<input type="checkbox" class="lt-checkbox" checked> Selected tickets only
</label>
</div>
<div class="lt-msg lt-msg-info">Exports include all visible columns.</div>
</div>
<div class="lt-modal-footer">
<button type="button" class="lt-btn lt-btn-ghost" data-modal-close>Cancel</button>
<button type="button" class="lt-btn lt-btn-primary" onclick="lt.toast.success('Export started'); lt.modal.close('export-modal')">Export</button>
</div>
</div>
</div>
<!-- ===========================================================
MODAL EXAMPLE: Keyboard shortcuts help
=========================================================== -->
<div id="lt-keys-help" class="lt-modal-overlay" aria-hidden="true">
<div class="lt-modal" role="dialog" aria-modal="true" aria-labelledby="keys-help-title">
<div class="lt-modal-header">
<span class="lt-modal-title" id="keys-help-title">Keyboard Shortcuts</span>
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close"></button>
</div>
<div class="lt-modal-body">
<table class="lt-data-table" style="width:100%">
<thead>
<tr><th scope="col">Shortcut</th><th scope="col">Action</th></tr>
</thead>
<tbody>
<tr><td>Ctrl / ⌘ + K</td><td>Focus search box</td></tr>
<tr><td>Ctrl / ⌘ + E</td><td>Toggle edit mode (ticket page)</td></tr>
<tr><td>Ctrl / ⌘ + S</td><td>Save changes (ticket page)</td></tr>
<tr><td>j / ↓</td><td>Select next row</td></tr>
<tr><td>k / ↑</td><td>Select previous row</td></tr>
<tr><td>Enter</td><td>Open selected ticket</td></tr>
<tr><td>n</td><td>New ticket</td></tr>
<tr><td>?</td><td>Show this help</td></tr>
<tr><td>ESC</td><td>Close modal / cancel</td></tr>
</tbody>
</table>
</div>
<div class="lt-modal-footer">
<button type="button" class="lt-btn" data-modal-close>Close</button>
</div>
</div>
</div>
<!-- ===========================================================
COMMAND PALETTE OVERLAY
=========================================================== -->
<div id="lt-cmd-overlay" class="lt-cmd-overlay" role="dialog" aria-modal="true" aria-label="Command palette">
<div class="lt-cmd-palette" id="lt-cmd-palette">
<div class="lt-cmd-input-wrap">
<span class="lt-cmd-prompt">&gt;</span>
<input id="lt-cmd-input" class="lt-cmd-input" type="text" placeholder="Search commands…" autocomplete="off" spellcheck="false" aria-label="Search commands">
</div>
<div class="lt-cmd-results" id="lt-cmd-results">
<div class="lt-cmd-empty">Start typing to search…</div>
</div>
<div class="lt-cmd-footer">
<span><kbd></kbd><kbd></kbd> Navigate</span>
<span><kbd>Enter</kbd> Select</span>
<span><kbd>Esc</kbd> Close</span>
</div>
</div>
</div>
<!-- ===========================================================
SCRIPTS
=========================================================== -->
<!--
PHP apps inject CSRF token + config here (with CSP nonce):
<script nonce="<?php echo $nonce; ?>">
window.CSRF_TOKEN = '<?php echo CsrfMiddleware::getToken(); ?>';
window.APP_TIMEZONE = '<?php echo $config['TIMEZONE']; ?>';
</script>
Node/Express (EJS):
<script nonce="<%= nonce %>">
window.CSRF_TOKEN = '<%= csrfToken %>';
</script>
Flask/Jinja2:
<script nonce="{{ nonce }}">
window.CSRF_TOKEN = '{{ csrf_token() }}';
</script>
-->
<!-- LotusGuild Terminal Design System JS v1.2 -->
<script src="/web_template/base.js"></script>
<!-- Init v1.2 modules -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// One call initializes accordion, tooltip, alerts, clipboard, sidebar, submenus + boot.
// Pass { boot: false } to skip the terminal boot animation.
lt.init({ bootName: 'MY APP' });
// Command palette demo commands
lt.cmdPalette.init([
{
group: 'Navigation',
items: [
{ icon: '⌂', label: 'Dashboard', kbd: 'G D', action: () => lt.toast.info('→ Dashboard') },
{ icon: '⊞', label: 'All Tickets', kbd: 'G T', action: () => lt.toast.info('→ Tickets') },
{ icon: '★', label: 'Saved Filters', action: () => lt.toast.info('→ Filters') },
]
},
{
group: 'Actions',
items: [
{ icon: '+', label: 'New Ticket', kbd: 'N', action: () => lt.toast.success('New ticket form') },
{ icon: '⤓', label: 'Export CSV', action: () => lt.modal.open('export-modal') },
{ icon: '⟳', label: 'Refresh Data', kbd: 'R', action: () => lt.toast.info('Refreshing...') },
]
},
{
group: 'Help',
items: [
{ icon: '?', label: 'Keyboard Shortcuts', kbd: '?', action: () => lt.modal.open('lt-keys-help') },
]
},
]);
// Range slider live value display
document.querySelectorAll('input[type="range"].lt-range').forEach(r => {
const wrap = r.closest('.lt-range-wrap');
const label = wrap && wrap.querySelector('.lt-range-value');
if (label) {
label.textContent = r.value;
r.addEventListener('input', () => { label.textContent = r.value; r.setAttribute('aria-valuenow', r.value); });
}
});
// Animate the demo progress bars on load
document.querySelectorAll('.lt-progress-bar[data-width]').forEach(bar => {
requestAnimationFrame(() => { bar.style.width = bar.dataset.width; });
});
// Theme toggle button
document.getElementById('lt-theme-btn').addEventListener('click', () => lt.theme.toggle());
// Context menu: register demo menu items
lt.contextMenu.register('demo-ctx', [
{ icon: '📋', label: 'Copy ID', kbd: 'C', action: () => lt.toast.info('ID copied') },
{ icon: '👁', label: 'View Details', action: () => lt.rightDrawer.open('lt-detail-drawer') },
{ icon: '✏️', label: 'Edit Ticket', kbd: 'E', action: () => lt.toast.info('Edit…') },
{ divider: true },
{ icon: '🗑', label: 'Delete', danger: true, action: () => lt.toast.error('Deleted') },
]);
// Programmatic context menu button
const demoCtxBtn = document.getElementById('demo-ctx-btn');
if (demoCtxBtn) demoCtxBtn.addEventListener('click', e => {
const r = demoCtxBtn.getBoundingClientRect();
lt.contextMenu.show(r.left, r.bottom + 4, [
{ icon: '📋', label: 'Copy ID', action: () => lt.toast.info('Copied') },
{ icon: '✏️', label: 'Edit', action: () => lt.toast.info('Edit') },
{ divider: true },
{ icon: '🗑', label: 'Delete', danger: true, action: () => lt.toast.error('Deleted') },
]);
});
// Combobox demo
lt.combobox.init(
document.getElementById('demo-combobox-input'),
[
{ value: 'worker-01', label: 'worker-01', icon: '🖥' },
{ value: 'worker-02', label: 'worker-02', icon: '🖥' },
{ value: 'worker-03', label: 'worker-03', icon: '🖥' },
{ value: 'gpu-01', label: 'gpu-01', icon: '⚡' },
{ value: 'gpu-02', label: 'gpu-02', icon: '⚡' },
{ value: 'storage-01',label: 'storage-01',icon: '💾' },
],
{ onChange: vals => console.log('[combobox]', vals) }
);
// Typeahead demo
lt.typeahead.init(
document.getElementById('demo-typeahead-input'),
[
{ value: '001', label: 'Link-down on compute-storage-01', icon: '🔴', meta: 'P1' },
{ value: '002', label: 'Switch port flapping USW-Pro-24', icon: '🟠', meta: 'P2' },
{ value: '003', label: 'Scheduled SFP+ replacement large1',icon: '🔵', meta: 'P3' },
{ value: '004', label: 'SSL cert renewal wiki.lotusguild.org', icon: '🟢', meta: 'P4' },
{ value: '005', label: 'RAID controller firmware update', icon: '🔴', meta: 'P1' },
],
{ onSelect: item => lt.toast.info(`Selected: ${item.label}`) }
);
// Split pane demo
lt.splitPane.init(document.getElementById('demo-split'), { initial: 0.4, minA: 100, minB: 100 });
// Demo notification badge initial count
lt.notif.set('#lt-notif-bell', 3);
// Wizard demo
lt.wizard.init(document.getElementById('demo-wizard'), {
onComplete: data => lt.toast.success('Ticket submitted: ' + (data.title || 'untitled')),
validate: (step, data) => {
if (step === 1 && !data.title?.trim()) { lt.toast.error('Title is required'); return false; }
return true;
},
});
// Sortable demo
const sortableList = lt.sortable.init(document.getElementById('demo-sortable'), {
onSort: (items) => {
document.getElementById('demo-sort-order').textContent = 'Order: ' + items.map(el => el.dataset.id).join(', ');
},
});
// Kanban drag-and-drop — all four columns share group "kanban"
['kanban-col-open','kanban-col-pending','kanban-col-inprogress','kanban-col-closed'].forEach(id => {
const col = document.getElementById(id);
if (col) lt.sortable.init(col, { group: 'kanban', onSort: () => {} });
});
// Pagination demo (50 items, 10 per page)
lt.pagination.init('#demo-pagination', {
total: 50, perPage: 10, page: 2,
onChange: p => lt.toast.info('Page ' + p),
});
// Countdown demo — SLA expires 2 hours from now
const slaTarget = new Date(Date.now() + 2 * 60 * 60 * 1000);
lt.timer.countdown(document.getElementById('demo-countdown'), slaTarget, {
urgent: 300,
urgentClass: 'lt-text-red lt-countdown-urgent',
onExpire: () => lt.toast.error('SLA BREACHED'),
});
// Stopwatch demo
const sw = lt.timer.stopwatch(document.getElementById('demo-stopwatch'));
let swRunning = true;
document.getElementById('sw-pause').addEventListener('click', function() {
if (swRunning) { sw.pause(); this.textContent = 'Resume'; } else { sw.resume(); this.textContent = 'Pause'; }
swRunning = !swRunning;
});
document.getElementById('sw-reset').addEventListener('click', () => { sw.reset(); swRunning = true; document.getElementById('sw-pause').textContent = 'Pause'; });
// Lightbox demo
lt.lightbox.init('.lt-lightbox-demo');
// Markdown demo
lt.markdown.init('#demo-markdown');
// Sidebar submenus (re-init for demo sidebar)
lt.sidebarSubmenus.init(document.querySelector('.lt-section-body'));
// ----- Notification bell dropdown -----
(function() {
const btn = document.getElementById('lt-notif-bell-btn');
const panel = document.getElementById('lt-notif-panel');
if (!btn || !panel) return;
function open() {
panel.removeAttribute('aria-hidden');
btn.setAttribute('aria-expanded', 'true');
// Mark all as read visually
}
function close(restoreFocus) {
panel.setAttribute('aria-hidden', 'true');
btn.setAttribute('aria-expanded', 'false');
if (restoreFocus) btn.focus();
}
btn.addEventListener('click', e => {
e.stopPropagation();
panel.hasAttribute('aria-hidden') ? open() : close();
});
// "Mark all read" button
const clearBtn = document.getElementById('lt-notif-clear-all');
if (clearBtn) clearBtn.addEventListener('click', () => {
panel.querySelectorAll('.lt-notif-item--unread').forEach(el => el.classList.remove('lt-notif-item--unread'));
panel.querySelectorAll('.lt-notif-dot').forEach(el => { el.classList.remove('lt-notif-dot'); el.classList.add('lt-notif-dot', 'lt-notif-dot--read'); });
lt.notif.clear('#lt-notif-bell');
lt.toast.info('All notifications marked as read');
});
// Individual item click + keyboard activation
panel.querySelectorAll('.lt-notif-item').forEach(item => {
const activate = () => {
item.classList.remove('lt-notif-item--unread');
const dot = item.querySelector('.lt-notif-dot');
if (dot) { dot.classList.add('lt-notif-dot--read'); }
close();
};
item.addEventListener('click', activate);
item.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); activate(); } });
});
// Close on outside click
document.addEventListener('click', e => {
if (!document.getElementById('lt-notif-bell').contains(e.target)) close();
});
// Esc close — restore focus to bell button
document.addEventListener('keydown', e => { if (e.key === 'Escape' && !panel.hasAttribute('aria-hidden')) close(true); });
}());
// ----- Generic dropdown toggle (Advanced filter + Bulk Actions) -----
document.querySelectorAll('.lt-dropdown-trigger').forEach(btn => {
const wrap = btn.closest('.lt-dropdown-wrap');
const panel = wrap && wrap.querySelector('.lt-dropdown-panel');
if (!panel) return;
btn.addEventListener('click', e => {
e.stopPropagation();
const isOpen = !panel.hasAttribute('aria-hidden');
// Close all other dropdowns first
document.querySelectorAll('.lt-dropdown-panel:not([aria-hidden])').forEach(p => {
p.setAttribute('aria-hidden', 'true');
const t = p.closest('.lt-dropdown-wrap').querySelector('.lt-dropdown-trigger');
if (t) t.setAttribute('aria-expanded', 'false');
});
if (!isOpen) {
panel.removeAttribute('aria-hidden');
btn.setAttribute('aria-expanded', 'true');
}
});
});
// Close dropdowns on outside click / Esc
document.addEventListener('click', () => {
document.querySelectorAll('.lt-dropdown-panel:not([aria-hidden])').forEach(p => {
p.setAttribute('aria-hidden', 'true');
const t = p.closest('.lt-dropdown-wrap').querySelector('.lt-dropdown-trigger');
if (t) t.setAttribute('aria-expanded', 'false');
});
});
document.addEventListener('keydown', e => {
if (e.key !== 'Escape') return;
document.querySelectorAll('.lt-dropdown-panel:not([aria-hidden])').forEach(p => {
p.setAttribute('aria-hidden', 'true');
const t = p.closest('.lt-dropdown-wrap').querySelector('.lt-dropdown-trigger');
if (t) { t.setAttribute('aria-expanded', 'false'); t.focus(); }
});
});
// SLA dismiss buttons — hide and persist per session
document.querySelectorAll('.lt-sla-dismiss').forEach(btn => {
const banner = btn.closest('.lt-sla-p1, .lt-sla-p2');
if (!banner) return;
const id = banner.dataset.slaId;
try { if (id && sessionStorage.getItem('lt_sla_dismissed_' + id)) banner.hidden = true; } catch (_) {}
btn.addEventListener('click', () => {
banner.hidden = true;
try { if (id) sessionStorage.setItem('lt_sla_dismissed_' + id, '1'); } catch (_) {}
});
});
// Footer year
const footerYear = document.getElementById('footer-year');
if (footerYear) footerYear.textContent = new Date().getFullYear();
// Tab bar switching
document.querySelectorAll('.lt-tab-bar').forEach(bar => {
bar.addEventListener('click', e => {
const tab = e.target.closest('.lt-tab');
if (!tab) return;
const targetId = tab.dataset.tabTarget;
if (!targetId) return;
const panels = bar.nextElementSibling;
bar.querySelectorAll('.lt-tab').forEach(t => { t.classList.remove('active'); t.setAttribute('aria-selected', 'false'); });
tab.classList.add('active');
tab.setAttribute('aria-selected', 'true');
if (panels) {
panels.querySelectorAll('.lt-tab-panel').forEach(p => p.classList.remove('active'));
const target = panels.querySelector('#' + targetId);
if (target) target.classList.add('active');
}
});
});
});
</script>
</body>
</html>