Files
scrying-pool/_bmad-output/planning-artifacts/ux-design-directions.html
T
2026-05-21 23:08:34 +02:00

818 lines
34 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>
<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 (240280px) — 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: 300500ms 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>