CLose story 1.2
This commit is contained in:
@@ -0,0 +1,817 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Scrying Pool — UX Design Directions</title>
|
||||
<style>
|
||||
:root {
|
||||
--sp-surface: #141618;
|
||||
--sp-surface-raised: #1c1f22;
|
||||
--sp-border: #282c30;
|
||||
--sp-text-primary: #dde2e8;
|
||||
--sp-text-secondary: #7a8390;
|
||||
--sp-accent: #4a9e6b;
|
||||
--sp-focus: #63c287;
|
||||
--sp-urgency-director: #c8982a;
|
||||
--sp-state-active: #4a9e6b;
|
||||
--sp-state-hidden: #6b7280;
|
||||
--sp-state-self-muted: #8b92a5;
|
||||
--sp-state-offline: #4b5563;
|
||||
--sp-state-cam-lost: #9ca3af;
|
||||
--sp-state-reconnecting: #c8982a;
|
||||
--sp-state-never-connected: #374151;
|
||||
--sp-state-ghost: #1f2937;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
background: #0e1012;
|
||||
color: var(--sp-text-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ── NAV ── */
|
||||
.nav {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
background: #0a0c0e;
|
||||
border-bottom: 1px solid var(--sp-border);
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
padding: 8px 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.nav h1 { font-size: 12px; font-weight: 600; color: var(--sp-accent); margin-right: 12px; white-space: nowrap; }
|
||||
.nav-btn {
|
||||
background: transparent; border: 1px solid var(--sp-border);
|
||||
color: var(--sp-text-secondary); border-radius: 4px;
|
||||
padding: 4px 10px; font-size: 11px; cursor: pointer; white-space: nowrap;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.nav-btn:hover, .nav-btn.active {
|
||||
background: var(--sp-surface-raised); border-color: var(--sp-accent);
|
||||
color: var(--sp-text-primary);
|
||||
}
|
||||
|
||||
/* ── LAYOUT ── */
|
||||
.stage { padding-top: 48px; }
|
||||
.direction {
|
||||
display: none; min-height: calc(100vh - 48px);
|
||||
padding: 24px;
|
||||
}
|
||||
.direction.visible { display: flex; gap: 24px; flex-wrap: wrap; }
|
||||
|
||||
.direction-label {
|
||||
width: 100%; padding-bottom: 8px; border-bottom: 1px solid var(--sp-border);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.direction-label h2 { font-size: 16px; font-weight: 600; color: var(--sp-text-primary); }
|
||||
.direction-label p { font-size: 12px; color: var(--sp-text-secondary); margin-top: 4px; }
|
||||
|
||||
/* ── FOUNDRY CHROME MOCK ── */
|
||||
.foundry-chrome {
|
||||
background: #1a1c1f;
|
||||
border: 1px solid #2a2d32;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.foundry-titlebar {
|
||||
background: #111315;
|
||||
padding: 6px 12px;
|
||||
font-size: 11px;
|
||||
color: #5a6070;
|
||||
border-bottom: 1px solid #222528;
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.foundry-titlebar span { color: var(--sp-accent); font-weight: 600; }
|
||||
|
||||
/* ── COMPACT STRIP ── */
|
||||
.strip {
|
||||
background: var(--sp-surface);
|
||||
border: 1px solid var(--sp-border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
min-width: 200px;
|
||||
}
|
||||
.strip-header {
|
||||
padding: 6px 10px;
|
||||
font-size: 10px; font-weight: 600; text-transform: uppercase;
|
||||
letter-spacing: 0.06em; color: var(--sp-text-secondary);
|
||||
border-bottom: 1px solid var(--sp-border);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.strip-header .sp-tag {
|
||||
color: var(--sp-accent); font-size: 9px;
|
||||
}
|
||||
.strip-row {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 0 10px; height: 32px;
|
||||
border-bottom: 1px solid #1e2124;
|
||||
position: relative; cursor: pointer;
|
||||
transition: background 0.1s;
|
||||
}
|
||||
.strip-row:hover { background: var(--sp-surface-raised); }
|
||||
.strip-row:hover .row-actions { opacity: 1; pointer-events: all; }
|
||||
.strip-row:last-child { border-bottom: none; }
|
||||
|
||||
.avatar {
|
||||
width: 24px; height: 24px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 9px; font-weight: 700; flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
.avatar.active { background: #1e3b2d; border: 2px solid var(--sp-state-active); }
|
||||
.avatar.hidden { background: #252729; border: 2px dashed var(--sp-state-hidden); opacity: 0.7; }
|
||||
.avatar.muted { background: #232530; border: 2px solid var(--sp-state-self-muted); }
|
||||
.avatar.offline { background: #1a1c1f; border: 2px solid var(--sp-state-offline); opacity: 0.5; }
|
||||
.avatar.reconnect { background: #2a2010; border: 2px solid var(--sp-state-reconnecting); }
|
||||
|
||||
.eye-slash {
|
||||
position: absolute; bottom: -1px; right: -1px;
|
||||
width: 10px; height: 10px; border-radius: 50%;
|
||||
background: var(--sp-surface);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 7px;
|
||||
}
|
||||
|
||||
.row-name {
|
||||
font-size: 13px; color: var(--sp-text-primary); flex: 1;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.row-name.dimmed { color: var(--sp-text-secondary); }
|
||||
|
||||
.state-chip {
|
||||
font-size: 9px; font-weight: 600; text-transform: uppercase;
|
||||
padding: 1px 5px; border-radius: 3px; letter-spacing: 0.04em;
|
||||
}
|
||||
.chip-active { background: #1e3b2d; color: var(--sp-state-active); }
|
||||
.chip-hidden { background: #252729; color: #9ca3af; }
|
||||
.chip-muted { background: #232530; color: var(--sp-state-self-muted); }
|
||||
.chip-reconnect { background: #2a2010; color: var(--sp-state-reconnecting); }
|
||||
|
||||
.row-actions {
|
||||
display: flex; gap: 4px;
|
||||
opacity: 0; pointer-events: none;
|
||||
transition: opacity 0.15s;
|
||||
position: absolute; right: 8px;
|
||||
}
|
||||
.action-btn {
|
||||
background: var(--sp-border); border: none;
|
||||
color: var(--sp-text-secondary); border-radius: 3px;
|
||||
padding: 2px 6px; font-size: 10px; cursor: pointer;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.action-btn:hover { background: var(--sp-accent); color: #000; }
|
||||
.action-btn.hide-btn:hover { background: #4b5563; color: var(--sp-text-primary); }
|
||||
|
||||
/* ── DIRECTOR'S BOARD ── */
|
||||
.board {
|
||||
background: var(--sp-surface);
|
||||
border: 1px solid var(--sp-border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
min-width: 340px;
|
||||
}
|
||||
.board-header {
|
||||
padding: 8px 12px;
|
||||
background: #111315;
|
||||
border-bottom: 1px solid var(--sp-border);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.board-title { font-size: 12px; font-weight: 600; color: var(--sp-text-primary); }
|
||||
.board-subtitle { font-size: 10px; color: var(--sp-text-secondary); margin-top: 1px; }
|
||||
.board-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||
gap: 1px; background: var(--sp-border);
|
||||
padding: 1px;
|
||||
}
|
||||
.board-card {
|
||||
background: var(--sp-surface-raised);
|
||||
padding: 10px 8px; text-align: center;
|
||||
cursor: pointer; transition: background 0.1s;
|
||||
position: relative;
|
||||
}
|
||||
.board-card:hover { background: #252830; }
|
||||
.board-card .card-avatar {
|
||||
width: 40px; height: 40px; border-radius: 50%;
|
||||
margin: 0 auto 6px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 14px; font-weight: 700;
|
||||
}
|
||||
.board-card .card-name { font-size: 10px; color: var(--sp-text-secondary); }
|
||||
.board-card .card-state { font-size: 9px; margin-top: 3px; }
|
||||
.board-card .card-toggle {
|
||||
position: absolute; top: 4px; right: 4px;
|
||||
font-size: 10px; opacity: 0;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
.board-card:hover .card-toggle { opacity: 1; }
|
||||
|
||||
/* ── PLAYER BADGE ── */
|
||||
.player-view {
|
||||
background: #1a1c20;
|
||||
border: 1px solid var(--sp-border);
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
min-width: 260px;
|
||||
}
|
||||
.player-view h3 { font-size: 11px; color: var(--sp-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 12px; }
|
||||
|
||||
.video-tile {
|
||||
background: #0d0f11;
|
||||
border-radius: 4px;
|
||||
aspect-ratio: 16/9;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
.video-tile.dimmed { opacity: 0.35; }
|
||||
|
||||
.visibility-badge {
|
||||
position: absolute; bottom: 6px; left: 6px;
|
||||
background: rgba(20,22,24,0.9);
|
||||
border: 1px solid var(--sp-border);
|
||||
border-radius: 10px;
|
||||
padding: 3px 8px 3px 6px;
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
font-size: 10px;
|
||||
backdrop-filter: blur(4px);
|
||||
cursor: pointer;
|
||||
}
|
||||
.badge-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.badge-dot.active { background: var(--sp-state-active); }
|
||||
.badge-dot.hidden { background: var(--sp-state-hidden); }
|
||||
.badge-dot.muted { background: var(--sp-state-self-muted); }
|
||||
|
||||
.first-encounter {
|
||||
margin-top: 8px;
|
||||
background: var(--sp-surface-raised);
|
||||
border: 1px solid var(--sp-border);
|
||||
border-radius: 4px;
|
||||
padding: 8px 10px;
|
||||
font-size: 11px; color: var(--sp-text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.first-encounter strong { color: var(--sp-text-primary); display: block; margin-bottom: 4px; }
|
||||
.got-it-btn {
|
||||
margin-top: 6px;
|
||||
background: var(--sp-accent); border: none;
|
||||
color: #000; border-radius: 3px;
|
||||
padding: 3px 10px; font-size: 10px; font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ── TOAST ── */
|
||||
.toast {
|
||||
background: #1c1f22;
|
||||
border: 1px solid var(--sp-border);
|
||||
border-left: 3px solid var(--sp-urgency-director);
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px; color: var(--sp-text-primary);
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.toast-icon { color: var(--sp-urgency-director); font-size: 14px; }
|
||||
|
||||
/* ── DIRECTION-SPECIFIC ── */
|
||||
|
||||
/* Direction 1: Docked vertical strip, minimal */
|
||||
.d1-layout {
|
||||
display: flex; gap: 16px; align-items: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
.d1-foundry {
|
||||
flex: 1; background: #16181b;
|
||||
border: 1px solid #222528; border-radius: 6px;
|
||||
height: 400px; position: relative;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #3a3f48; font-size: 13px;
|
||||
}
|
||||
.d1-strip-dock {
|
||||
position: absolute; right: 0; top: 0; bottom: 0;
|
||||
width: 220px; border-left: 1px solid var(--sp-border);
|
||||
background: var(--sp-surface);
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
|
||||
/* Direction 2: Floating strip */
|
||||
.d2-floating-strip {
|
||||
position: absolute; right: 16px; top: 16px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.6);
|
||||
border-radius: 6px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Direction 3: Avatar-only condensed strip */
|
||||
.strip-compact {
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
padding: 6px;
|
||||
background: var(--sp-surface);
|
||||
border: 1px solid var(--sp-border);
|
||||
border-radius: 6px;
|
||||
width: 44px;
|
||||
}
|
||||
.avatar-only {
|
||||
width: 32px; height: 32px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 700;
|
||||
cursor: pointer; position: relative;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
.avatar-only:hover { transform: scale(1.1); }
|
||||
.avatar-only.active { background: #1e3b2d; border: 2px solid var(--sp-state-active); }
|
||||
.avatar-only.hidden { background: #252729; border: 2px dashed var(--sp-state-hidden); opacity: 0.65; }
|
||||
.avatar-only.reconnect { background: #2a2010; border: 2px solid var(--sp-state-reconnecting); }
|
||||
.avatar-only.offline { background: #1a1c1f; border: 2px solid var(--sp-state-offline); opacity: 0.4; }
|
||||
|
||||
/* ── ANNOTATION ── */
|
||||
.annotation {
|
||||
background: #0f1113;
|
||||
border: 1px solid #222528;
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
font-size: 11px;
|
||||
color: var(--sp-text-secondary);
|
||||
line-height: 1.6;
|
||||
min-width: 220px;
|
||||
max-width: 300px;
|
||||
}
|
||||
.annotation strong { color: var(--sp-text-primary); display: block; margin-bottom: 4px; font-size: 12px; }
|
||||
.annotation .pro { color: var(--sp-state-active); }
|
||||
.annotation .con { color: #e57373; }
|
||||
.annotation ul { padding-left: 14px; margin: 4px 0; }
|
||||
.annotation li { margin-bottom: 2px; }
|
||||
|
||||
/* ── SECTION HEADERS ── */
|
||||
.section-row {
|
||||
display: flex; gap: 16px; align-items: flex-start;
|
||||
flex-wrap: wrap; width: 100%;
|
||||
}
|
||||
|
||||
/* PULSE ANIMATION */
|
||||
@keyframes pulse-ring {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
.avatar.reconnect { animation: pulse-ring 2s ease-in-out infinite; }
|
||||
.avatar-only.reconnect { animation: pulse-ring 2s ease-in-out infinite; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.avatar.reconnect, .avatar-only.reconnect { animation: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="nav">
|
||||
<h1>🔮 Scrying Pool</h1>
|
||||
<button class="nav-btn active" onclick="show(1)">D1 · Docked Strip</button>
|
||||
<button class="nav-btn" onclick="show(2)">D2 · Floating Strip</button>
|
||||
<button class="nav-btn" onclick="show(3)">D3 · Avatar-Only Strip</button>
|
||||
<button class="nav-btn" onclick="show(4)">D4 · Director's Board Primary</button>
|
||||
<button class="nav-btn" onclick="show(5)">D5 · Rich Row Strip</button>
|
||||
<button class="nav-btn" onclick="show(6)">D6 · Player View</button>
|
||||
</nav>
|
||||
|
||||
<div class="stage">
|
||||
|
||||
<!-- ════════════════════════════════════════════ DIRECTION 1 ═══ -->
|
||||
<div class="direction visible" id="d1">
|
||||
<div class="direction-label" style="width:100%">
|
||||
<h2>Direction 1 — Docked Vertical Strip (Expert Path)</h2>
|
||||
<p>Compact strip permanently docked to Foundry's right panel. Always visible, zero chrome, ambient authority. Expert-first; novice learns through Learning Bridge.</p>
|
||||
</div>
|
||||
|
||||
<div style="position:relative; width:600px; height:400px; flex-shrink:0;">
|
||||
<div class="d1-foundry" style="width:100%; height:100%;">
|
||||
← Canvas / Map area →
|
||||
<div class="d1-strip-dock">
|
||||
<div class="strip-header">
|
||||
Scrying Pool <span class="sp-tag">● Live</span>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar active">M</div>
|
||||
<span class="row-name">Marcus</span>
|
||||
<span class="state-chip chip-active">Live</span>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn hide-btn">Hide</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar hidden">
|
||||
S
|
||||
<span class="eye-slash">👁</span>
|
||||
</div>
|
||||
<span class="row-name dimmed">Sofia</span>
|
||||
<span class="state-chip chip-hidden">Hidden</span>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn">Show</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar muted">J</div>
|
||||
<span class="row-name">Jake</span>
|
||||
<span class="state-chip chip-muted">Muted</span>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn hide-btn">Hide</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar reconnect">A</div>
|
||||
<span class="row-name">Alex</span>
|
||||
<span class="state-chip chip-reconnect">Rejoining</span>
|
||||
<div class="row-actions">
|
||||
<button class="action-btn hide-btn">Hide</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar offline">?</div>
|
||||
<span class="row-name dimmed">Player 5</span>
|
||||
<span class="state-chip" style="color:#4b5563">Offline</span>
|
||||
</div>
|
||||
<div style="padding:8px 10px; margin-top:auto; border-top:1px solid var(--sp-border);">
|
||||
<button style="width:100%; padding:5px; background:var(--sp-surface-raised); border:1px solid var(--sp-border); border-radius:3px; color:var(--sp-text-secondary); font-size:10px; cursor:pointer;">
|
||||
Open Director's Board ↗
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; flex-direction:column; gap:12px; max-width:280px;">
|
||||
<div class="annotation">
|
||||
<strong>Direction 1 — Docked Strip</strong>
|
||||
Key behaviours:
|
||||
<ul>
|
||||
<li>Hover row → action rail slides in from right</li>
|
||||
<li>Right-click → context menu (fast path)</li>
|
||||
<li>Strip always open; no toggle needed</li>
|
||||
<li>Strip → Director's Board via footer CTA</li>
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pro">✓ Zero extra chrome; ambient state overview always visible</span><br>
|
||||
<span class="pro">✓ Expert path natively; strip teaches itself on first hover</span><br>
|
||||
<span class="con">✗ Requires docking API support in v14</span><br>
|
||||
<span class="con">✗ Competes with Foundry's native right panel</span>
|
||||
</div>
|
||||
<div class="toast">
|
||||
<span class="toast-icon">◈</span>
|
||||
<span>Scrying Pool: Sofia visibility updated</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ DIRECTION 2 ═══ -->
|
||||
<div class="direction" id="d2">
|
||||
<div class="direction-label" style="width:100%">
|
||||
<h2>Direction 2 — Floating Strip (ApplicationV2 Default)</h2>
|
||||
<p>Strip as a small floating ApplicationV2 window. Draggable, resizable, position persisted. Keyboard shortcut to open/close. Default open state true.</p>
|
||||
</div>
|
||||
|
||||
<div style="position:relative; width:600px; height:420px; flex-shrink:0; background:#16181b; border:1px solid #222528; border-radius:6px; overflow:hidden; display:flex; align-items:center; justify-content:center; color:#3a3f48; font-size:13px;">
|
||||
← Canvas / Map area →
|
||||
|
||||
<div class="d2-floating-strip strip" style="min-width:200px;">
|
||||
<div class="strip-header" style="cursor:move;">
|
||||
🔮 Scrying Pool <span class="sp-tag">● 4 live</span>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar active">M</div>
|
||||
<span class="row-name">Marcus</span>
|
||||
<div class="row-actions"><button class="action-btn hide-btn">Hide</button></div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar hidden">S<span class="eye-slash">👁</span></div>
|
||||
<span class="row-name dimmed">Sofia</span>
|
||||
<div class="row-actions"><button class="action-btn">Show</button></div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar muted">J</div>
|
||||
<span class="row-name">Jake</span>
|
||||
<div class="row-actions"><button class="action-btn hide-btn">Hide</button></div>
|
||||
</div>
|
||||
<div class="strip-row">
|
||||
<div class="avatar reconnect">A</div>
|
||||
<span class="row-name">Alex</span>
|
||||
<div class="row-actions"><button class="action-btn hide-btn">Hide</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; flex-direction:column; gap:12px; max-width:280px;">
|
||||
<div class="annotation">
|
||||
<strong>Direction 2 — Floating Strip</strong>
|
||||
Key behaviours:
|
||||
<ul>
|
||||
<li>Opens with keyboard shortcut (single chord)</li>
|
||||
<li>Draggable titlebar; resize from edges</li>
|
||||
<li>Position/open state → GM User flag</li>
|
||||
<li>Stays on top of canvas; can be minimised</li>
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pro">✓ Zero footprint until opened; no panel conflict</span><br>
|
||||
<span class="pro">✓ Standard ApplicationV2 pattern — trivial to implement</span><br>
|
||||
<span class="con">✗ GM must remember shortcut; discoverability gap</span><br>
|
||||
<span class="con">✗ Occludes map when open at wrong position</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ DIRECTION 3 ═══ -->
|
||||
<div class="direction" id="d3">
|
||||
<div class="direction-label" style="width:100%">
|
||||
<h2>Direction 3 — Avatar-Only Condensed Strip</h2>
|
||||
<p>Ultra-compact vertical strip showing only 32px avatars with state rings. Click to expand to a full-detail popover. Maximum density, minimum screen real-estate.</p>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; gap:16px; align-items:flex-start;">
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--sp-text-secondary); text-transform:uppercase; letter-spacing:.06em; margin-bottom:8px;">Collapsed state</div>
|
||||
<div class="strip-compact">
|
||||
<div class="avatar-only active">M</div>
|
||||
<div class="avatar-only hidden" style="position:relative;">
|
||||
S
|
||||
<span style="position:absolute;bottom:-1px;right:-1px;font-size:7px;background:var(--sp-surface);border-radius:50%;width:10px;height:10px;display:flex;align-items:center;justify-content:center;">👁</span>
|
||||
</div>
|
||||
<div class="avatar-only" style="background:#232530;border:2px solid var(--sp-state-self-muted);">J</div>
|
||||
<div class="avatar-only reconnect">A</div>
|
||||
<div class="avatar-only offline" style="font-size:9px;">?</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--sp-text-secondary); text-transform:uppercase; letter-spacing:.06em; margin-bottom:8px;">On avatar click → popover</div>
|
||||
<div style="background:var(--sp-surface); border:1px solid var(--sp-border); border-radius:6px; padding:10px 12px; min-width:200px; box-shadow:0 4px 16px rgba(0,0,0,.5);">
|
||||
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
|
||||
<div class="avatar hidden" style="width:32px;height:32px;font-size:13px;">S<span class="eye-slash">👁</span></div>
|
||||
<div>
|
||||
<div style="font-size:13px; font-weight:600;">Sofia</div>
|
||||
<div style="font-size:10px; color:var(--sp-text-secondary);">Not visible to others</div>
|
||||
</div>
|
||||
</div>
|
||||
<button style="width:100%; padding:6px; background:var(--sp-accent); border:none; border-radius:3px; color:#000; font-size:11px; font-weight:600; cursor:pointer;">Show to table</button>
|
||||
<button style="width:100%; padding:6px; background:var(--sp-surface-raised); border:1px solid var(--sp-border); border-radius:3px; color:var(--sp-text-secondary); font-size:11px; cursor:pointer; margin-top:4px;">Add note…</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annotation">
|
||||
<strong>Direction 3 — Avatar-Only</strong>
|
||||
Key behaviours:
|
||||
<ul>
|
||||
<li>Strip width: 44px — near-zero footprint</li>
|
||||
<li>All detail in popover on click/hover</li>
|
||||
<li>State ring + icon only — no text labels</li>
|
||||
<li>Works inside Foundry AV strip space</li>
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pro">✓ Absolute minimum screen real-estate</span><br>
|
||||
<span class="pro">✓ At-a-glance state by ring shape/colour</span><br>
|
||||
<span class="con">✗ Copy/state labels hidden by default — accessibility concern</span><br>
|
||||
<span class="con">✗ Popover adds click cost for every action</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ DIRECTION 4 ═══ -->
|
||||
<div class="direction" id="d4">
|
||||
<div class="direction-label" style="width:100%">
|
||||
<h2>Direction 4 — Director's Board as Primary Surface</h2>
|
||||
<p>Director's Board (Level 2) promoted as the main GM control surface. Compact strip becomes a secondary summary-only view. Board-first = more information density per action.</p>
|
||||
</div>
|
||||
|
||||
<div class="board" style="min-width:360px; max-width:440px;">
|
||||
<div class="board-header">
|
||||
<div>
|
||||
<div class="board-title">🔮 Director's Board</div>
|
||||
<div class="board-subtitle">Scrying Pool · 5 participants · 3 visible</div>
|
||||
</div>
|
||||
<button style="background:var(--sp-accent);border:none;border-radius:3px;color:#000;font-size:10px;font-weight:600;padding:3px 8px;cursor:pointer;">Hide All</button>
|
||||
</div>
|
||||
<div class="board-grid">
|
||||
<div class="board-card">
|
||||
<div class="card-avatar active" style="background:#1e3b2d;border:2px solid var(--sp-state-active);">M</div>
|
||||
<div class="card-name">Marcus</div>
|
||||
<div class="card-state" style="color:var(--sp-state-active);">● Live</div>
|
||||
<div class="card-toggle">👁</div>
|
||||
</div>
|
||||
<div class="board-card">
|
||||
<div class="card-avatar" style="background:#252729;border:2px dashed var(--sp-state-hidden);opacity:.65;">S</div>
|
||||
<div class="card-name" style="color:var(--sp-text-secondary);">Sofia</div>
|
||||
<div class="card-state" style="color:#9ca3af;">✕ Hidden</div>
|
||||
<div class="card-toggle">🚫</div>
|
||||
</div>
|
||||
<div class="board-card">
|
||||
<div class="card-avatar" style="background:#232530;border:2px solid var(--sp-state-self-muted);">J</div>
|
||||
<div class="card-name">Jake</div>
|
||||
<div class="card-state" style="color:var(--sp-state-self-muted);">🔇 Muted</div>
|
||||
<div class="card-toggle">👁</div>
|
||||
</div>
|
||||
<div class="board-card">
|
||||
<div class="card-avatar reconnect" style="background:#2a2010;border:2px solid var(--sp-state-reconnecting);width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;animation:pulse-ring 2s ease-in-out infinite;">A</div>
|
||||
<div class="card-name">Alex</div>
|
||||
<div class="card-state" style="color:var(--sp-state-reconnecting);">↺ Rejoining</div>
|
||||
<div class="card-toggle">👁</div>
|
||||
</div>
|
||||
<div class="board-card" style="opacity:.5;">
|
||||
<div class="card-avatar" style="background:#1a1c1f;border:2px solid var(--sp-state-offline);width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;">?</div>
|
||||
<div class="card-name" style="color:var(--sp-text-secondary);">Player 5</div>
|
||||
<div class="card-state" style="color:#4b5563;">✕ Offline</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:8px 12px; border-top:1px solid var(--sp-border); display:flex; gap:8px;">
|
||||
<button style="flex:1; padding:5px; background:var(--sp-surface-raised); border:1px solid var(--sp-border); border-radius:3px; color:var(--sp-text-secondary); font-size:10px; cursor:pointer;">Show All</button>
|
||||
<button style="flex:1; padding:5px; background:var(--sp-surface-raised); border:1px solid var(--sp-border); border-radius:3px; color:var(--sp-text-secondary); font-size:10px; cursor:pointer;">Save Preset…</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annotation">
|
||||
<strong>Direction 4 — Board Primary</strong>
|
||||
Key behaviours:
|
||||
<ul>
|
||||
<li>Board is default open surface for GMs</li>
|
||||
<li>Card click = toggle visibility</li>
|
||||
<li>Hover → card-level toggle icon</li>
|
||||
<li>Compact strip = read-only summary</li>
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pro">✓ Optimal for bulk operations (Level 2)</span><br>
|
||||
<span class="pro">✓ Video thumbnails can live here</span><br>
|
||||
<span class="con">✗ Heavier to open than strip for single-player changes</span><br>
|
||||
<span class="con">✗ Board should remain Level 2 opt-in — making it primary changes the product story</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ DIRECTION 5 ═══ -->
|
||||
<div class="direction" id="d5">
|
||||
<div class="direction-label" style="width:100%">
|
||||
<h2>Direction 5 — Rich Row Strip with Inline State Labels</h2>
|
||||
<p>Wider strip showing avatar + name + full state label + action. Maximum information at a glance. Best for novice GMs who need copy to orient them.</p>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; gap:16px; align-items:flex-start;">
|
||||
<div class="strip" style="min-width:260px;">
|
||||
<div class="strip-header">
|
||||
🔮 Scrying Pool
|
||||
<span class="sp-tag">5 participants</span>
|
||||
</div>
|
||||
|
||||
<!-- Rich rows with state label column -->
|
||||
<div class="strip-row" style="height:36px;">
|
||||
<div class="avatar active">M</div>
|
||||
<div style="flex:1; display:flex; flex-direction:column; justify-content:center;">
|
||||
<span style="font-size:12px; color:var(--sp-text-primary); line-height:1.2;">Marcus</span>
|
||||
<span style="font-size:10px; color:var(--sp-state-active);">Visible to all</span>
|
||||
</div>
|
||||
<div class="row-actions"><button class="action-btn hide-btn">Hide from table</button></div>
|
||||
</div>
|
||||
|
||||
<div class="strip-row" style="height:36px; background:rgba(107,114,128,0.05);">
|
||||
<div class="avatar hidden">S<span class="eye-slash">👁</span></div>
|
||||
<div style="flex:1; display:flex; flex-direction:column; justify-content:center;">
|
||||
<span style="font-size:12px; color:var(--sp-text-secondary); line-height:1.2;">Sofia</span>
|
||||
<span style="font-size:10px; color:#9ca3af;">Not visible to others</span>
|
||||
</div>
|
||||
<div class="row-actions"><button class="action-btn">Show to table</button></div>
|
||||
</div>
|
||||
|
||||
<div class="strip-row" style="height:36px;">
|
||||
<div class="avatar muted">J</div>
|
||||
<div style="flex:1; display:flex; flex-direction:column; justify-content:center;">
|
||||
<span style="font-size:12px; color:var(--sp-text-primary); line-height:1.2;">Jake</span>
|
||||
<span style="font-size:10px; color:var(--sp-state-self-muted);">Camera off (player choice)</span>
|
||||
</div>
|
||||
<div class="row-actions"><button class="action-btn hide-btn">Hide from table</button></div>
|
||||
</div>
|
||||
|
||||
<div class="strip-row" style="height:36px;">
|
||||
<div class="avatar reconnect">A</div>
|
||||
<div style="flex:1; display:flex; flex-direction:column; justify-content:center;">
|
||||
<span style="font-size:12px; color:var(--sp-text-primary); line-height:1.2;">Alex</span>
|
||||
<span style="font-size:10px; color:var(--sp-state-reconnecting);">Rejoining view…</span>
|
||||
</div>
|
||||
<div class="row-actions"><button class="action-btn hide-btn">Hide from table</button></div>
|
||||
</div>
|
||||
|
||||
<div class="strip-row" style="height:36px; opacity:.5;">
|
||||
<div class="avatar offline">?</div>
|
||||
<div style="flex:1; display:flex; flex-direction:column; justify-content:center;">
|
||||
<span style="font-size:12px; color:var(--sp-text-secondary); line-height:1.2;">Player 5</span>
|
||||
<span style="font-size:10px; color:#4b5563;">Disconnected</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annotation">
|
||||
<strong>Direction 5 — Rich Row Strip</strong>
|
||||
Key behaviours:
|
||||
<ul>
|
||||
<li>Row height 36px (vs 32px) for two-line label</li>
|
||||
<li>State copy uses player-facing vocabulary</li>
|
||||
<li>Action label is canonical: "Hide from table"</li>
|
||||
<li>Best combined with novice primary path</li>
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pro">✓ Self-teaching — no tooltip or docs needed</span><br>
|
||||
<span class="pro">✓ Canonical copy reinforced in GM strip</span><br>
|
||||
<span class="con">✗ Wider strip (240–280px) — larger screen footprint</span><br>
|
||||
<span class="con">✗ Copy density may feel verbose at 8+ participants</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ DIRECTION 6 ═══ -->
|
||||
<div class="direction" id="d6">
|
||||
<div class="direction-label" style="width:100%">
|
||||
<h2>Direction 6 — Player View: VisibilityBadge & First Encounter</h2>
|
||||
<p>The player's experience: badge on their own video tile; first-encounter explanation; ongoing state legibility. This is what Sofia sees.</p>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; gap:20px; flex-wrap:wrap; align-items:flex-start;">
|
||||
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--sp-text-secondary); text-transform:uppercase; letter-spacing:.06em; margin-bottom:8px;">State: Active (visible)</div>
|
||||
<div class="player-view" style="max-width:240px;">
|
||||
<div class="video-tile" style="height:135px;">
|
||||
👤
|
||||
<div class="visibility-badge">
|
||||
<div class="badge-dot active"></div>
|
||||
<span style="color:var(--sp-text-primary);">Visible to all</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--sp-text-secondary); text-transform:uppercase; letter-spacing:.06em; margin-bottom:8px;">State: Hidden (first encounter)</div>
|
||||
<div class="player-view" style="max-width:260px;">
|
||||
<div class="video-tile dimmed" style="height:135px;">
|
||||
👤
|
||||
<div class="visibility-badge" style="border-color:var(--sp-state-hidden);">
|
||||
<div class="badge-dot hidden"></div>
|
||||
<span style="color:var(--sp-text-secondary);">Not visible to others</span>
|
||||
<span style="color:var(--sp-text-secondary); font-size:9px; margin-left:2px;">?</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="first-encounter">
|
||||
<strong>What is this badge?</strong>
|
||||
This badge shows who can currently see your camera. Click anytime to learn more.
|
||||
<br>
|
||||
<button class="got-it-btn">Got it</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-size:10px; color:var(--sp-text-secondary); text-transform:uppercase; letter-spacing:.06em; margin-bottom:8px;">State: Reconnecting</div>
|
||||
<div class="player-view" style="max-width:240px;">
|
||||
<div class="video-tile" style="height:135px; background:#0f110d;">
|
||||
👤
|
||||
<div class="visibility-badge" style="border-color:var(--sp-state-reconnecting); animation:pulse-ring 2s ease-in-out infinite;">
|
||||
<div class="badge-dot" style="background:var(--sp-state-reconnecting);"></div>
|
||||
<span style="color:var(--sp-urgency-director);">Rejoining view…</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annotation" style="max-width:260px;">
|
||||
<strong>Direction 6 — Player View</strong>
|
||||
Key behaviours:
|
||||
<ul>
|
||||
<li>Badge: <code>position:absolute</code> bottom-left of tile</li>
|
||||
<li>Fade: 300–500ms on player's own tile only</li>
|
||||
<li>First encounter: pulsed badge + "Got it" panel</li>
|
||||
<li>Badge always clickable for state explanation</li>
|
||||
<li>Audio never affected by any state change</li>
|
||||
<li>Copy: dignity-first vocabulary throughout</li>
|
||||
</ul>
|
||||
<br>
|
||||
<span class="pro">✓ Player always informed; never blindsided</span><br>
|
||||
<span class="pro">✓ "Got it" sets firstGMActivation (not dismiss)</span><br>
|
||||
<span class="con">✗ Badge must not obscure face at 24px tile size</span><br>
|
||||
<em style="font-size:10px; color:#7a8390;">Consider: bottom-left vs bottom-right placement based on AV grid layout testing</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /stage -->
|
||||
|
||||
<script>
|
||||
function show(n) {
|
||||
document.querySelectorAll('.direction').forEach(d => d.classList.remove('visible'));
|
||||
document.getElementById('d' + n).classList.add('visible');
|
||||
document.querySelectorAll('.nav-btn').forEach((b, i) => {
|
||||
b.classList.toggle('active', i === n - 1);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user