diff --git a/README.md b/README.md
index a72de03..904a212 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,10 @@ No database. No auth. No external services. Just add your API keys and go.
---
+## Screenshot
+
+
+
## Consensus Validation Protocol
### Purpose
@@ -270,10 +274,6 @@ The codebase has been built with defense-in-depth in mind — server-side person
---
-## Screenshot
-
-
-
## Features
| Feature | Description |
diff --git a/app/globals.css b/app/globals.css
index 784663f..c488dfd 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -2,82 +2,541 @@
@tailwind components;
@tailwind utilities;
+/* ── Root color tokens ──────────────────────────────────── */
+:root {
+ --rt-bg: #02070f;
+ --rt-bg-2: #050d22;
+ --rt-bg-3: #081634;
+ --rt-blue: #003087;
+ --rt-blue-soft: #4d7ac7;
+ --rt-orange: #ff6200;
+ --rt-orange-soft: #ff9a4d;
+ --rt-text: #f1f5ff;
+ --rt-muted: #8b9cb8;
+ --rt-header-h: 65px;
+}
+
+@media (min-width: 640px) {
+ :root {
+ --rt-header-h: 73px;
+ }
+}
+
+html,
+body {
+ background: var(--rt-bg);
+ color: var(--rt-text);
+ font-family:
+ "Inter",
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ system-ui,
+ sans-serif;
+ font-feature-settings: "ss01", "cv11", "cv02";
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ letter-spacing: -0.005em;
+}
+
+/* ── Cosmic background scaffold ─────────────────────────── */
+.cosmic-shell {
+ position: relative;
+ min-height: 100vh;
+ isolation: isolate;
+ background:
+ radial-gradient(ellipse 90% 60% at 18% 4%, rgba(0, 48, 135, 0.38), transparent 65%),
+ radial-gradient(ellipse 70% 50% at 88% 12%, rgba(255, 98, 0, 0.1), transparent 60%),
+ radial-gradient(ellipse 100% 70% at 50% 110%, rgba(0, 48, 135, 0.3), transparent 70%),
+ linear-gradient(180deg, #02070f 0%, #030a1c 35%, #050e26 70%, #02070f 100%);
+}
+
+/* Static orange top-glow — paint-once. */
+.cosmic-shell::before {
+ content: "";
+ position: absolute;
+ inset: 0 0 auto 0;
+ height: 55vh;
+ pointer-events: none;
+ z-index: 0;
+ background:
+ radial-gradient(ellipse 60% 22% at 30% 8%, rgba(255, 98, 0, 0.22), transparent 72%),
+ radial-gradient(ellipse 55% 18% at 65% 14%, rgba(255, 154, 77, 0.18), transparent 75%),
+ radial-gradient(ellipse 50% 14% at 50% 22%, rgba(255, 98, 0, 0.1), transparent 80%);
+ filter: blur(2px);
+ opacity: 0.95;
+ contain: paint;
+}
+
+/* Single, slow, GPU-composited ray sweep — animates only `transform`. */
+.cosmic-shell::after {
+ content: "";
+ position: absolute;
+ inset: -10% -20%;
+ pointer-events: none;
+ z-index: 0;
+ background-image:
+ linear-gradient(
+ 105deg,
+ transparent 38%,
+ rgba(255, 138, 51, 0.1) 48%,
+ rgba(255, 98, 0, 0.16) 50%,
+ rgba(255, 138, 51, 0.1) 52%,
+ transparent 62%
+ ),
+ linear-gradient(
+ 98deg,
+ transparent 32%,
+ rgba(77, 122, 199, 0.08) 49%,
+ rgba(126, 165, 230, 0.14) 50%,
+ rgba(77, 122, 199, 0.08) 51%,
+ transparent 68%
+ );
+ opacity: 0.6;
+ will-change: transform;
+ transform: translate3d(0, 0, 0);
+ animation: raySlide 36s linear infinite;
+ contain: paint;
+}
+
+@keyframes raySlide {
+ 0% {
+ transform: translate3d(-3%, 0, 0);
+ }
+ 100% {
+ transform: translate3d(3%, 0, 0);
+ }
+}
+
+/* Subtle starfield — opacity-only twinkle, very cheap. */
+.cosmic-stars {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ z-index: 0;
+ background-image:
+ radial-gradient(1.5px 1.5px at 12% 18%, rgba(241, 245, 255, 0.55), transparent 60%),
+ radial-gradient(1px 1px at 22% 64%, rgba(241, 245, 255, 0.4), transparent 60%),
+ radial-gradient(1.5px 1.5px at 38% 28%, rgba(255, 154, 77, 0.45), transparent 60%),
+ radial-gradient(1px 1px at 48% 82%, rgba(241, 245, 255, 0.35), transparent 60%),
+ radial-gradient(2px 2px at 62% 12%, rgba(241, 245, 255, 0.5), transparent 60%),
+ radial-gradient(1px 1px at 72% 56%, rgba(126, 165, 230, 0.45), transparent 60%),
+ radial-gradient(1.5px 1.5px at 84% 34%, rgba(241, 245, 255, 0.4), transparent 60%),
+ radial-gradient(1px 1px at 92% 78%, rgba(255, 154, 77, 0.5), transparent 60%),
+ radial-gradient(1px 1px at 8% 88%, rgba(241, 245, 255, 0.4), transparent 60%),
+ radial-gradient(1.5px 1.5px at 56% 46%, rgba(241, 245, 255, 0.35), transparent 60%);
+ animation: twinkle 12s ease-in-out infinite alternate;
+ will-change: opacity;
+}
+
+@keyframes twinkle {
+ 0% {
+ opacity: 0.55;
+ }
+ 100% {
+ opacity: 0.9;
+ }
+}
+
+/* Floating orbs — translate-only, GPU layer. */
+.cosmic-orbs {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ z-index: 0;
+ overflow: hidden;
+ contain: layout paint;
+}
+.cosmic-orbs::before,
+.cosmic-orbs::after {
+ content: "";
+ position: absolute;
+ border-radius: 50%;
+ filter: blur(56px);
+ opacity: 0.45;
+ will-change: transform;
+ transform: translate3d(0, 0, 0);
+ backface-visibility: hidden;
+}
+.cosmic-orbs::before {
+ width: 460px;
+ height: 460px;
+ top: -160px;
+ left: -120px;
+ background: radial-gradient(circle, rgba(0, 48, 135, 0.6), transparent 70%);
+ animation: orbFloat1 40s ease-in-out infinite;
+}
+.cosmic-orbs::after {
+ width: 400px;
+ height: 400px;
+ bottom: -160px;
+ right: -100px;
+ background: radial-gradient(circle, rgba(255, 98, 0, 0.28), transparent 70%);
+ animation: orbFloat2 48s ease-in-out infinite;
+}
+
+@keyframes orbFloat1 {
+ 0%,
+ 100% {
+ transform: translate3d(0, 0, 0);
+ }
+ 50% {
+ transform: translate3d(40px, 30px, 0);
+ }
+}
+@keyframes orbFloat2 {
+ 0%,
+ 100% {
+ transform: translate3d(0, 0, 0);
+ }
+ 50% {
+ transform: translate3d(-36px, -22px, 0);
+ }
+}
+
+/* ── Reduced-motion override ───────────────────────────── */
+@media (prefers-reduced-motion: reduce) {
+ .cosmic-shell::after,
+ .cosmic-stars,
+ .cosmic-orbs::before,
+ .cosmic-orbs::after,
+ .progress-orange,
+ .animate-in,
+ .shine::after {
+ animation: none !important;
+ transition: none !important;
+ }
+ .cosmic-stars {
+ opacity: 0.7;
+ }
+}
+
+/* Phones: lower the cost ceiling further. */
+@media (hover: none) and (max-width: 640px) {
+ .cosmic-shell::after {
+ animation-duration: 60s;
+ opacity: 0.45;
+ }
+ .cosmic-orbs::before {
+ filter: blur(40px);
+ }
+ .cosmic-orbs::after {
+ filter: blur(40px);
+ }
+}
+
+/* ── Glass utilities ─────────────────────────────────────
+ IMPORTANT: backdrop-filter is the most expensive CSS effect
+ when stacked across many elements that paint together with
+ scrolling/animated content behind them. We restrict it to
+ transient or single-instance fixed layers (header, modal,
+ drawer, toaster, sticky progress bar) and use opaque-ish
+ gradients with a luminous border for the rest of the cards.
+ Visually it reads as glass against our largely-static cosmic
+ background while keeping compositing cheap. */
+
+.glass {
+ background: linear-gradient(135deg, rgba(8, 22, 52, 0.85) 0%, rgba(4, 12, 30, 0.92) 100%);
+ border: 1px solid rgba(77, 122, 199, 0.22);
+ border-radius: 22px;
+ box-shadow:
+ 0 12px 32px -10px rgba(0, 0, 0, 0.6),
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.05);
+}
+
+.glass-soft {
+ background: linear-gradient(135deg, rgba(8, 22, 52, 0.78) 0%, rgba(4, 12, 30, 0.85) 100%);
+ border: 1px solid rgba(77, 122, 199, 0.16);
+ border-radius: 18px;
+ box-shadow:
+ 0 6px 20px -8px rgba(0, 0, 0, 0.5),
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.04);
+}
+
+.glass-strong {
+ background: linear-gradient(135deg, rgba(10, 26, 60, 0.92) 0%, rgba(5, 14, 36, 0.96) 100%);
+ border: 1px solid rgba(77, 122, 199, 0.32);
+ border-radius: 24px;
+ box-shadow:
+ 0 20px 48px -16px rgba(0, 0, 0, 0.7),
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.06);
+}
+
+.glass-input {
+ background: linear-gradient(135deg, rgba(2, 8, 22, 0.85), rgba(4, 12, 30, 0.78));
+ border: 1px solid rgba(77, 122, 199, 0.2);
+ border-radius: 14px;
+ transition:
+ border-color 200ms ease,
+ box-shadow 200ms ease;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
+}
+.glass-input:hover {
+ border-color: rgba(77, 122, 199, 0.4);
+}
+.glass-input:focus,
+.glass-input:focus-within {
+ border-color: rgba(255, 98, 0, 0.6);
+ box-shadow:
+ 0 0 0 3px rgba(255, 98, 0, 0.08),
+ 0 0 18px -6px rgba(255, 98, 0, 0.35),
+ inset 0 1px 0 rgba(255, 255, 255, 0.05);
+ outline: none;
+}
+
+/* Small inline pills are infrequent + tiny — cheap to blur. */
+.glass-pill {
+ background: linear-gradient(135deg, rgba(8, 22, 52, 0.55), rgba(4, 12, 30, 0.55));
+ border: 1px solid rgba(77, 122, 199, 0.22);
+ border-radius: 999px;
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+}
+
+/* For genuinely fixed/sticky surfaces that benefit from a real
+ blur of whatever scrolls behind. Use sparingly. */
+.glass-fixed {
+ background: linear-gradient(180deg, rgba(2, 7, 15, 0.7), rgba(2, 7, 15, 0.55));
+ border: 1px solid rgba(77, 122, 199, 0.18);
+ backdrop-filter: blur(20px) saturate(140%);
+ -webkit-backdrop-filter: blur(20px) saturate(140%);
+}
+
+.glass-divider {
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(77, 122, 199, 0.4), transparent);
+}
+
+.btn-orange {
+ background: linear-gradient(180deg, #ff8a3a 0%, #ff6200 60%, #e25400 100%);
+ color: #fff;
+ border-radius: 14px;
+ border: 1px solid rgba(255, 154, 77, 0.55);
+ box-shadow:
+ 0 1px 0 rgba(255, 255, 255, 0.18) inset,
+ 0 -1px 0 rgba(0, 0, 0, 0.25) inset,
+ 0 8px 22px -6px rgba(255, 98, 0, 0.55);
+ transition:
+ transform 180ms ease,
+ box-shadow 180ms ease;
+ font-weight: 600;
+ letter-spacing: 0.005em;
+}
+.btn-orange:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow:
+ 0 1px 0 rgba(255, 255, 255, 0.22) inset,
+ 0 -1px 0 rgba(0, 0, 0, 0.25) inset,
+ 0 12px 28px -6px rgba(255, 98, 0, 0.7);
+}
+.btn-orange:active:not(:disabled) {
+ transform: translateY(0);
+}
+.btn-orange:disabled {
+ opacity: 0.35;
+ cursor: not-allowed;
+ filter: saturate(0.6);
+}
+
+.btn-ghost {
+ background: linear-gradient(135deg, rgba(8, 22, 52, 0.7), rgba(4, 12, 30, 0.7));
+ border: 1px solid rgba(77, 122, 199, 0.25);
+ border-radius: 12px;
+ color: var(--rt-text);
+ transition:
+ border-color 180ms ease,
+ color 180ms ease,
+ box-shadow 180ms ease;
+}
+.btn-ghost:hover:not(:disabled) {
+ border-color: rgba(255, 98, 0, 0.45);
+ box-shadow: 0 0 14px -4px rgba(255, 98, 0, 0.35);
+ color: var(--rt-orange-soft);
+}
+
+/* Toggle */
+.glass-toggle {
+ width: 32px;
+ height: 18px;
+ border-radius: 999px;
+ background: linear-gradient(135deg, rgba(8, 22, 52, 0.65), rgba(4, 12, 30, 0.7));
+ border: 1px solid rgba(77, 122, 199, 0.3);
+ position: relative;
+ transition:
+ background 200ms ease,
+ border-color 200ms ease,
+ box-shadow 200ms ease;
+ flex-shrink: 0;
+}
+.glass-toggle.on {
+ background: linear-gradient(135deg, #ff8a3a, #ff6200);
+ border-color: rgba(255, 154, 77, 0.7);
+ box-shadow: 0 0 12px -2px rgba(255, 98, 0, 0.6);
+}
+.glass-toggle .knob {
+ position: absolute;
+ top: 50%;
+ left: 2px;
+ transform: translateY(-50%);
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background: #f1f5ff;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
+ transition: left 200ms cubic-bezier(0.16, 1, 0.3, 1);
+}
+.glass-toggle.on .knob {
+ left: 16px;
+ background: #ffffff;
+}
+
+/* Section header */
+.section-label {
+ font-size: 9.5px;
+ font-weight: 600;
+ color: rgba(139, 156, 184, 0.85);
+ text-transform: uppercase;
+ letter-spacing: 0.18em;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+}
+
+/* Animated orange progress bar — single GPU-composited
+ background-position shift. Cheap. */
+.progress-orange {
+ background: linear-gradient(90deg, #ff6200 0%, #ff9a4d 50%, #ff6200 100%);
+ background-size: 200% 100%;
+ animation: shimmer 2.4s linear infinite;
+ box-shadow: 0 0 12px rgba(255, 98, 0, 0.45);
+ will-change: background-position;
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
+}
+
/* ── Scrollbar ──────────────────────────────────────── */
::-webkit-scrollbar {
- width: 5px;
+ width: 6px;
+ height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
- background: #47556860;
+ background: rgba(77, 122, 199, 0.3);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
- background: #47556890;
+ background: rgba(255, 98, 0, 0.5);
}
* {
- scrollbar-color: #47556860 transparent;
+ scrollbar-color: rgba(77, 122, 199, 0.3) transparent;
}
/* ── Prose / Markdown ───────────────────────────────── */
.prose pre {
- background: #0f172a !important;
- border: 1px solid #334155;
- border-radius: 8px;
+ background: rgba(2, 7, 15, 0.85) !important;
+ border: 1px solid rgba(77, 122, 199, 0.2);
+ border-radius: 12px;
}
.prose code {
font-size: 0.85em;
+ color: var(--rt-orange-soft);
}
.prose a {
- color: #93c5fd;
+ color: var(--rt-orange-soft);
text-decoration: none;
}
.prose a:hover {
text-decoration: underline;
+ color: var(--rt-orange);
}
.prose blockquote {
- border-left-color: #60a5fa;
+ border-left-color: var(--rt-orange);
color: #cbd5e1;
}
.prose strong {
- color: #f1f5f9;
+ color: var(--rt-text);
}
.prose h1,
.prose h2,
.prose h3 {
- color: #f1f5f9;
+ color: var(--rt-text);
}
.prose li::marker {
- color: #64748b;
+ color: rgba(255, 98, 0, 0.6);
}
/* ── Animations ─────────────────────────────────────── */
-@keyframes shimmer {
- 0% {
- background-position: 200% 0;
- }
- 100% {
- background-position: -200% 0;
- }
-}
-
.animate-in {
- animation: fadeInUp 350ms cubic-bezier(0.16, 1, 0.3, 1) both;
+ animation: fadeInUp 380ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes fadeInUp {
from {
opacity: 0;
- transform: translateY(8px) scale(0.98);
+ transform: translate3d(0, 8px, 0);
}
to {
opacity: 1;
- transform: translateY(0) scale(1);
+ transform: translate3d(0, 0, 0);
}
}
-/* ── Selection ──────────────────────────────────────── */
+/* Result card containment: completed cards are static once done,
+ so the browser can skip layout/paint when scrolled out of view. */
+.card-result {
+ contain: layout paint style;
+ content-visibility: auto;
+ contain-intrinsic-size: auto 360px;
+}
+
+/* Streaming card stays painted (its content keeps changing) but
+ its layout/paint are still contained to itself. */
+.card-streaming {
+ contain: layout paint style;
+}
+
+/* Selection */
::selection {
- background: rgba(96, 165, 250, 0.25);
- color: #f1f5f9;
+ background: rgba(255, 98, 0, 0.35);
+ color: #fff;
+}
+
+/* Focus ring */
+*:focus-visible {
+ outline: 1px solid rgba(255, 98, 0, 0.7);
+ outline-offset: 2px;
+ border-radius: 6px;
+}
+
+/* Subtle shine sweep on hover */
+.shine {
+ position: relative;
+ overflow: hidden;
+}
+.shine::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: -100%;
+ height: 100%;
+ width: 80%;
+ background: linear-gradient(
+ 100deg,
+ transparent 0%,
+ rgba(255, 255, 255, 0.06) 50%,
+ transparent 100%
+ );
+ transition: left 700ms ease;
+ pointer-events: none;
+}
+.shine:hover::after {
+ left: 120%;
}
diff --git a/app/layout.tsx b/app/layout.tsx
index 4236ad1..7d88041 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,4 +1,4 @@
-import type { Metadata } from "next";
+import type { Metadata, Viewport } from "next";
import { Toaster } from "sonner";
import "./globals.css";
@@ -17,19 +17,58 @@ export const metadata: Metadata = {
manifest: "/site.webmanifest",
};
+export const viewport: Viewport = {
+ width: "device-width",
+ initialScale: 1,
+ maximumScale: 5,
+ viewportFit: "cover",
+ themeColor: "#02070F",
+};
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
-
+
+ {/* ── Fixed cosmic atmosphere — paint once, GPU-composited ───────── */}
+
+
+ {/* Faint starfield (opacity-only twinkle) */}
+
+ {/* Two slow drifting orbs (translate-only, smaller blur) */}
+
+
{children}
diff --git a/app/page.tsx b/app/page.tsx
index 6e1bc5a..1068bf8 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,7 +1,7 @@
"use client";
// ─────────────────────────────────────────────────────────────
-// RoundTable — Main Page (Clean Dashboard)
+// RoundTable — Main Page (Glassmorphic Enterprise Dashboard)
// ─────────────────────────────────────────────────────────────
import { useEffect, useCallback, useState } from "react";
@@ -16,6 +16,7 @@ import ClaimsPanel from "@/components/ClaimsPanel";
import CostMeter from "@/components/CostMeter";
import ConfigPanel from "@/components/ConfigPanel";
import PromptLibrary from "@/components/PromptLibrary";
+import { ConsensusNodesArt, ConfigArt } from "@/components/HeroArt";
import { toast } from "sonner";
import SweepResultsPanel from "@/components/SweepResultsPanel";
import {
@@ -24,13 +25,15 @@ import {
Settings2,
Minus,
Plus,
- Hexagon,
Square,
Users,
ArrowRight,
Sparkles,
Eye,
Layers,
+ Cpu,
+ Menu,
+ X,
} from "lucide-react";
import type { ConsensusEvent, ConsensusRequest, EngineType } from "@/lib/types";
import { decodeSnapshotFromHash } from "@/lib/session";
@@ -55,6 +58,27 @@ export default function HomePage() {
const loadSnapshot = useArenaStore((s) => s.loadSnapshot);
const [showOnboarding, setShowOnboarding] = useState(true);
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ // Lock body scroll when the mobile drawer is open
+ useEffect(() => {
+ if (typeof document === "undefined") return;
+ const prev = document.body.style.overflow;
+ if (drawerOpen) document.body.style.overflow = "hidden";
+ return () => {
+ document.body.style.overflow = prev;
+ };
+ }, [drawerOpen]);
+
+ // Close drawer on Escape
+ useEffect(() => {
+ if (!drawerOpen) return;
+ const handler = (e: KeyboardEvent) => {
+ if (e.key === "Escape") setDrawerOpen(false);
+ };
+ window.addEventListener("keydown", handler);
+ return () => window.removeEventListener("keydown", handler);
+ }, [drawerOpen]);
useEffect(() => {
if (!showOnboarding) return;
@@ -80,7 +104,6 @@ export default function HomePage() {
});
}, [setAvailableModels, setModelsLoading]);
- // Load a shared snapshot from the URL hash, if present
useEffect(() => {
if (typeof window === "undefined") return;
if (!window.location.hash) return;
@@ -181,6 +204,7 @@ export default function HomePage() {
// Clear any previous sweep state when a single-engine run starts
useArenaStore.getState().clearSweep();
+ setDrawerOpen(false);
toast.info("Consensus started — Esc to cancel");
await runOneEngine();
}, [runOneEngine]);
@@ -207,6 +231,7 @@ export default function HomePage() {
const sweepEngines: EngineType[] = ["cvp", "blind-jury", "adversarial"];
const store = useArenaStore.getState();
store.startSweep(sweepEngines);
+ setDrawerOpen(false);
toast.info(
`Sweep started — running ${sweepEngines.length} engines sequentially. Esc cancels current.`,
);
@@ -256,20 +281,145 @@ export default function HomePage() {
reset();
}, [reset]);
+ // ── Sidebar content (used both inline at lg+ and inside the mobile drawer) ──
+ const sidebarContent = (
+
+
+
+
+
+
+
+
+
+
+ Configuration
+
+
Provider, model & persona
+
+
+
+
+
+
+
+
+
+ Rounds
+
+
+ {options.engine === "blind-jury" ? "Locked at 1" : "1–10"}
+
+
+
+
+
+
+ {options.engine === "blind-jury" ? 1 : options.rounds}
+
+
+ debate rounds
+
+
+
+
+
+
+
+
+ {/* Sub-xl panels: visible whenever the right rail is hidden */}
+
+
+
+
+
+
+
+ {isRunning && (
+
+
+
+
+
+
+
+ Round {currentRound} of {options.rounds}
+
+
+
+
+
+ Press{" "}
+
+ Esc
+ {" "}
+ to cancel
+
+
+ )}
+
+ {finalScore !== null && (
+
+
+ Final Consensus
+
+
+ {finalScore}%
+
+
+
+ )}
+
+ );
+
return (
-
+
{/* Shared-view banner */}
{sharedView && (
-
-
-
-
Viewing a shared session. Reset to start your own run.
+
+
+
+ Viewing a shared session.
)}
@@ -277,19 +427,33 @@ export default function HomePage() {
{/* Onboarding */}
{showOnboarding && participants.length === 0 && !sharedView && (
setShowOnboarding(false)}
+ role="dialog"
+ aria-modal="true"
+ aria-labelledby="onboarding-title"
>
-
-
-
+
+
-
Add Participants to Begin
-
- Select AI providers and models from the sidebar, assign personas, then enter a prompt.
-
-
-
Sidebar
+
+
+ Convene the RoundTable
+
+
+ Open the panel, pick a model and persona, then enter your prompt to start a multi-AI
+ consensus debate.
+
+
+
+
Panel
Model
@@ -302,153 +466,177 @@ export default function HomePage() {
)}
{/* Header */}
-
-
-
-
-
- RoundTable
-
-
- Multi-AI Consensus Playground
-
+
+
+
+ {/* Mobile menu button — visible below lg */}
+
+
+ {/* Logo mark */}
+
+
+
+ RoundTable
+
+
+ Multi-AI Consensus Playground
+
+
+ Consensus Playground
+
+
+
+
+
-
- Protocol inspired by askgrokmcp
-
{/* Main Layout */}
-
- {/* Sidebar */}
-