diff --git a/control-app.html b/control-app.html
index 89d69126..1a80ff3a 100644
--- a/control-app.html
+++ b/control-app.html
@@ -3,11 +3,11 @@
-
+
-
+
OpenMouse Control
diff --git a/control.html b/control.html
index a6e2a286..17de87a7 100644
--- a/control.html
+++ b/control.html
@@ -3,11 +3,11 @@
-
+
-
+
OpenMouse License
diff --git a/demo.html b/demo.html
index 221a6cae..0c9770f5 100644
--- a/demo.html
+++ b/demo.html
@@ -3,13 +3,13 @@
-
+
-
+
diff --git a/index.html b/index.html
index e80fd1a4..7e8f9712 100644
--- a/index.html
+++ b/index.html
@@ -3,10 +3,10 @@
-
+
-
+
diff --git a/package.json b/package.json
index 0bdac19c..f5e178f0 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"dev": "vite",
"build": "tsc --noEmit && vite build",
"test": "node --test src/egg-we-protocol.test.ts src/egg-op1-protocol.test.ts",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "test:landing": "node --test tests/landing-page.test.mjs"
},
"devDependencies": {
"typescript": "^5.8.3",
diff --git a/src/control-gate.ts b/src/control-gate.ts
index 994b7bc1..3f5bce49 100644
--- a/src/control-gate.ts
+++ b/src/control-gate.ts
@@ -16,25 +16,24 @@ function escapeMarkup(value: string): string {
function renderGate(message = ""): void {
appRoot.innerHTML = `
-
-
-
-
OpenMouse
-
${__BUILD_CHANNEL__.toUpperCase()} · v${__APP_VERSION__}
-
- PRIVATE CONTROL PANEL
- Enter your license.
- Use your OpenMouse license code to unlock device control.
-
+
+
+
+ OM
+ Private control panel
+ Enter your license.
+ Use your OpenMouse license code to unlock verified device controls in this browser.
+
+
+
`;
document.querySelector("#access-form")?.addEventListener("submit", async (event) => {
diff --git a/src/control.css b/src/control.css
index cf86b813..1aeed2e8 100644
--- a/src/control.css
+++ b/src/control.css
@@ -1,24 +1,21 @@
@import "./demo.css";
-.access-gate {
- display: flex;
- min-height: 100vh;
- align-items: flex-start;
- justify-content: center;
- flex-direction: column;
- width: min(100% - 3rem, 520px);
- margin: 0 auto;
-}
-
-.access-gate .demo-wordmark { margin-bottom: 5rem; }
-.access-gate h1 { margin: 0; font-size: clamp(2.8rem, 8vw, 4.6rem); font-weight: 500; letter-spacing: -.04em; line-height: .92; }
-.access-gate > p:not(.overline) { max-width: 420px; color: #96969b; line-height: 1.6; }
-.access-form { display: grid; width: min(100%, 360px); gap: .6rem; margin-top: 1.4rem; }
-.access-form label { color: #96969b; font-size: .72rem; }
-.access-form input { padding: .8rem .9rem; border: 1px solid #36363a; border-radius: 8px; outline: none; background: #161618; color: #f7f7f7; }
-.access-form input:focus { border-color: #f7f7f7; }
-.access-form button { padding: .8rem .9rem; border: 0; border-radius: 8px; background: #f7f7f7; color: #09090b; font-weight: 600; }
-.access-form output { min-height: 1.2rem; color: #ee8c85; font-size: .72rem; }
+.access-shell { display: grid; width: min(calc(100% - 2rem), 760px); min-height: 100vh; margin: 0 auto; padding: 1.5rem 0; grid-template-rows: auto 1fr auto; }
+.access-header { display: flex; align-items: center; justify-content: space-between; min-height: 48px; }
+.access-header .demo-wordmark { margin: 0; }
+.build-meta { color: var(--muted); font-family: inherit; font-size: .58rem; font-weight: 600; letter-spacing: -0.15px; line-height: 1; text-transform: uppercase; white-space: nowrap; }
+.access-gate { align-self: center; width: min(100%, 540px); padding: clamp(2rem, 7vw, 4rem); border: 1px solid var(--border); border-radius: var(--radius-lg); background: radial-gradient(circle at 0 0, rgb(255 255 255 / 7%), transparent 38%), var(--surface-deep); box-shadow: 0 30px 80px rgb(0 0 0 / 32%); }
+.access-mark { display: grid; width: 42px; height: 42px; margin-bottom: 3.5rem; place-items: center; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); font-size: .68rem; font-weight: 600; }
+.access-gate h1 { margin: 0; font-size: clamp(2.8rem, 8vw, 4.6rem); font-weight: 500; letter-spacing: -0.15px; line-height: .94; }
+.access-gate > p:not(.overline) { max-width: 420px; margin: 1.25rem 0 0; color: var(--muted); line-height: 1.65; }
+.access-form { display: grid; width: 100%; gap: .65rem; margin-top: 2rem; }
+.access-form label { color: var(--muted); font-size: .7rem; }
+.access-form input { min-height: 46px; padding: 0 .9rem; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); outline: none; background: var(--surface); color: var(--text); }
+.access-form input:focus { border-color: var(--text); box-shadow: 0 0 0 3px rgb(255 255 255 / 7%); }
+.access-form button { min-height: 46px; padding: 0 .9rem; border: 0; border-radius: var(--radius-sm); background: var(--text); color: var(--background); font-weight: 600; }
+.access-form button:disabled { cursor: wait; opacity: .65; }
+.access-form output { min-height: 1.2rem; color: var(--negative); font-size: .7rem; line-height: 1.5; }
+.access-footnote { align-self: end; margin: 1rem 0 0; color: var(--muted); font-size: .64rem; text-align: center; }
.control-shell { display: grid; grid-template-columns: 260px minmax(0, 1fr); min-height: 100vh; }
.device-select { cursor: default; }
.setting-card button:disabled { cursor: default; opacity: 1; }
@@ -93,3 +90,96 @@
.control-shell .sidebar-footer { display: none; }
.control-shell .control-panel { width: min(100% - 2rem, 1180px); }
}
+
+/* Neutral precision control surface */
+.control-shell { background: var(--background); color: var(--text); }
+.control-shell .sidebar { border-color: var(--border); background: var(--surface-deep); }
+.control-shell .device-select,
+.control-shell .preview-banner,
+.control-shell .setting-card,
+.control-shell .panel-footer,
+.control-shell .egg-collapsible,
+.control-shell .egg-experimental,
+.control-shell .interface-setting-card { border-color: var(--border); background: var(--surface-deep); }
+.control-shell .device-overview { border-color: var(--border); border-radius: var(--radius-lg); background: var(--surface-deep); }
+.control-shell .summary-stat,
+.control-shell .quick-stats,
+.control-shell .toggle-row,
+.control-shell .interface-settings-header,
+.control-shell .egg-collapsible-body,
+.control-shell .egg-experimental-body { border-color: var(--border) !important; }
+.control-shell .setting-card,
+.control-shell .egg-collapsible,
+.control-shell .egg-experimental,
+.control-shell .interface-setting-card { border-radius: var(--radius-md) !important; }
+.control-shell .preview-banner,
+.control-shell .panel-footer,
+.control-shell .sidebar-action,
+.control-shell .empty-connect-action,
+.control-shell .setting-action button,
+.control-shell .dpi-header-actions button,
+.control-shell .dpi-header-actions input,
+.control-shell .segmented button,
+.control-shell .egg-action-button,
+.control-shell [data-apply-cpi],
+.control-shell .interface-settings-back,
+.control-shell .interface-reset,
+.control-shell input:not([type="checkbox"]):not([type="range"]),
+.control-shell select { border-radius: var(--radius-sm) !important; }
+.control-shell .sidebar-action,
+.control-shell .dpi-header-actions button,
+.control-shell .dpi-header-actions input,
+.control-shell .segmented button,
+.control-shell .egg-action-button,
+.control-shell [data-apply-cpi],
+.control-shell .interface-settings-back,
+.control-shell input:not([type="checkbox"]):not([type="range"]),
+.control-shell select { border-color: var(--border-strong) !important; background: var(--surface) !important; color: var(--text) !important; }
+.control-shell .sidebar-action:hover,
+.control-shell .segmented button:hover,
+.control-shell .egg-action-button:hover,
+.control-shell [data-apply-cpi]:hover { background: var(--surface-hover) !important; }
+.control-shell .segmented button.selected,
+.control-shell .setting-action button:not(:disabled),
+.control-shell .dpi-header-actions button:not(:disabled) { border-color: var(--ui-accent) !important; background: var(--ui-accent) !important; color: var(--ui-accent-ink) !important; }
+.control-shell .device-label,
+.control-shell .overline,
+.control-shell .summary-stat > span,
+.control-shell .setting-heading p,
+.control-shell .setting-note,
+.control-shell .range-labels,
+.control-shell .sidebar-footer,
+.control-shell .panel-footer,
+.control-shell .interface-setting-card > span,
+.control-shell .interface-setting-card p { color: var(--muted); font-family: inherit; letter-spacing: -0.15px; }
+.control-shell .panel-header h1,
+.control-shell .empty-state h2,
+.control-shell .interface-settings-header h2 { letter-spacing: -0.15px; }
+.control-shell .interface-settings-page { background: var(--background); }
+.control-shell.is-empty .empty-state { border-color: var(--border); border-radius: var(--radius-lg); background: radial-gradient(circle at 76% 48%, var(--ui-accent-soft), transparent 23%), linear-gradient(135deg, var(--surface), var(--surface-deep) 68%); }
+.control-shell .device-dot.is-idle,
+.control-shell .status-dot.is-idle { background: var(--muted); }
+.control-shell .panel-footer #read-status,
+.control-shell .device-status,
+.control-shell .empty-state > p:not(.overline),
+.control-shell .empty-state small { color: var(--muted); }
+.control-scroll-region { position: relative; overflow-y: auto; }
+.compact-action { align-self: flex-start; min-height: 28px; margin-top: .45rem; padding: 0 .55rem; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); font-size: .61rem; font-weight: 600; }
+.device-detail-item,
+.device-field-card { min-width: 0; padding: .6rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
+.device-detail-item small { display: block; margin-bottom: .25rem; color: var(--muted); font-size: .52rem; }
+.device-detail-item span { color: var(--text); font-size: .67rem; font-weight: 600; overflow-wrap: anywhere; }
+.device-field-card > strong { font-size: .7rem; }
+.device-field-card label { display: block; margin-top: .4rem; color: var(--muted); font-size: .58rem; }
+.device-field-card input:not([type="checkbox"]),
+.device-field-card select { width: 100%; min-height: 34px; box-sizing: border-box; margin-top: .18rem; padding: 0 .45rem; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface-deep); color: var(--text); }
+.device-field-card .check-field { display: flex; align-items: center; gap: .35rem; font-size: .62rem; }
+.paired-fields { display: grid; grid-template-columns: 1fr 1fr; gap: .35rem; }
+.device-field-card [data-apply-cpi] { min-height: 30px; margin-top: .45rem; padding: 0 .55rem; }
+
+@media (max-width: 560px) {
+ .access-shell { padding-top: 1rem; }
+ .access-header { align-items: flex-start; gap: .75rem; }
+ .access-gate { padding: 1.5rem; }
+ .access-mark { margin-bottom: 2.5rem; }
+}
diff --git a/src/control.ts b/src/control.ts
index 9f94816b..48991a32 100644
--- a/src/control.ts
+++ b/src/control.ts
@@ -142,7 +142,7 @@ function renderControl(): void {
.control-shell { --ui-accent:#69d28d;--ui-accent-ink:#07120b;--ui-accent-soft:rgb(105 210 141 / 16%) }
.build-identity { display:flex;align-items:center;gap:.65rem;margin-bottom:4rem }
.build-identity .demo-wordmark { margin-bottom:0 }
- .build-badge { display:inline-flex;align-items:center;min-height:1.35rem;padding:.2rem .45rem;border:1px solid color-mix(in srgb,var(--ui-accent) 35%,transparent);border-radius:999px;background:var(--ui-accent-soft);color:var(--ui-accent);font-family:"JetBrains Mono",ui-monospace,monospace;font-size:.52rem;font-weight:700;letter-spacing:.07em;line-height:1;white-space:nowrap }
+ .build-meta { color:var(--muted);font-family:inherit;font-size:.52rem;font-weight:700;letter-spacing:-.15px;line-height:1;text-transform:uppercase;white-space:nowrap }
.control-shell[data-interface-theme="violet"] { --ui-accent:#a78bfa;--ui-accent-ink:#130b25;--ui-accent-soft:rgb(167 139 250 / 17%) }
.control-shell[data-interface-theme="ice"] { --ui-accent:#67d8ff;--ui-accent-ink:#06161d;--ui-accent-soft:rgb(103 216 255 / 16%) }
.control-shell[data-interface-theme="ember"] { --ui-accent:#ff9b62;--ui-accent-ink:#211006;--ui-accent-soft:rgb(255 155 98 / 17%) }
@@ -213,7 +213,7 @@ function renderControl(): void {
#egg-cpi-stage-list, #egg-button-list { grid-template-columns:1fr !important }
}
.interface-settings-button::before { content:"⚙";font-size:.72rem }
- .interface-settings-page { position:absolute;z-index:20;inset:0;display:none;padding:1rem 0 2rem;overflow:auto;background:#0b0b0d;scrollbar-width:none }
+ .interface-settings-page { position:absolute;z-index:20;inset:0;display:none;padding:1rem 0 2rem;overflow:auto;background:var(--background);scrollbar-width:none }
.interface-settings-page::-webkit-scrollbar { display:none }
.interface-settings-page.is-open { display:block }
.interface-settings-header { display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;padding:1rem 0 1.2rem;border-bottom:1px solid #29292d }
@@ -226,7 +226,7 @@ function renderControl(): void {
.interface-setting-card p { margin:0 0 .8rem;color:#85858a;font-size:.68rem;line-height:1.45 }
.interface-setting-card select { width:100%;padding:.52rem;border:1px solid #39393e;border-radius:7px;background:#18181b;color:#eee;color-scheme:dark }
.theme-preview { display:flex;gap:.32rem;margin-top:.65rem }
- .theme-preview i { width:1.25rem;height:.28rem;border-radius:999px;background:var(--ui-accent);opacity:.2 }
+ .theme-preview i { width:1.25rem;height:2px;border-radius:0;background:var(--ui-accent);opacity:.2 }
.theme-preview i:nth-child(2) { opacity:.35 }.theme-preview i:nth-child(3) { opacity:.55 }.theme-preview i:nth-child(4) { opacity:.75 }.theme-preview i:nth-child(5) { opacity:1 }
.interface-switch-row { display:flex;align-items:center;justify-content:space-between;gap:.8rem;margin-top:.7rem;color:#c5c5c9;font-size:.72rem }
.interface-switch-row input {
@@ -236,7 +236,7 @@ function renderControl(): void {
flex:0 0 auto;
margin:0;
border:1px solid #414147;
- border-radius:999px;
+ border-radius:var(--radius-sm);
outline:none;
background-color:#242428;
background-image:radial-gradient(circle at .56rem 50%,#a0a0a5 0 .34rem,transparent .36rem);
@@ -249,6 +249,11 @@ function renderControl(): void {
background-image:radial-gradient(circle at calc(100% - .56rem) 50%,#07120b 0 .34rem,transparent .36rem);
}
.interface-switch-row input:focus-visible { box-shadow:0 0 0 3px var(--ui-accent-soft) }
+ .binary-setting-row { display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.2rem 0;color:#b3b3b7;font-size:.7rem }
+ .binary-switch { min-width:42px;padding:.28rem .5rem;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface);color:var(--muted);font-size:.58rem }
+ .binary-switch[aria-checked="true"] { border-color:var(--ui-accent);background:var(--ui-accent);color:var(--ui-accent-ink) }
+ .binary-switch:disabled { border-color:var(--border);background:var(--surface-deep);color:#66666b }
+ .binary-switch:focus-visible { outline:2px solid var(--ui-accent);outline-offset:2px }
.interface-reset { margin-top:.75rem;padding:.55rem .75rem;border:1px solid #4a3436;border-radius:7px;background:#1c1315;color:#e7a7aa;font-size:.68rem;font-weight:650 }
.density-comfortable #pulsar-advanced.egg-advanced-layout { gap:.8rem !important }
.density-comfortable #pulsar-advanced.egg-advanced-layout > .setting-card { padding:1rem !important }
@@ -269,7 +274,7 @@ function renderControl(): void {