CLose story 1.2

This commit is contained in:
2026-05-21 23:08:34 +02:00
commit 110b295a7b
75 changed files with 16065 additions and 0 deletions
@@ -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 (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>