Refresh the scene dashboard around a crew bubble column

Replace the roster banner and the standalone character-sheet panel with
a left-hand column of crew bubbles. Each bubble shows the player's role
icon, name, rank, current hand size, and a 🛞 for the Captain; clicking
one pops out that player's character sheet as a modal.

- New CrewColumn.svelte: bubbles (Pi-Rats first, Deep last) + an
  Objectives button that opens a crew-objectives popup.
- CharacterSheet.svelte is now a target-driven modal: description,
  likes/hates, and personal objectives for anyone; secret J/Q/K only on
  your own sheet; the Duel UI (no "Challenge whom?" — target is fixed)
  only when a living Pi-Rat views another living Pi-Rat.
- ScenePhase.svelte → 3-column layout (crew | obstacles | hand/deep);
  crew column collapses to a horizontal strip on narrow screens.
- Shared .modal-backdrop/.modal-box/.modal-close moved into components.css;
  removed the now-dead roster-banner and captain-badge styles.

Backend unchanged — the state blob already carries hands, roles, ranks,
objectives, and the captain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 20:03:41 -07:00
parent 8614a6c820
commit 2ea91c066a
6 changed files with 367 additions and 236 deletions

View File

@@ -398,25 +398,6 @@
color: var(--ghost);
}
.captain-badge {
background: color-mix(in srgb, var(--accent) 14%, transparent);
border: 1px solid var(--accent);
color: var(--accent);
padding: 0.4rem 1rem;
border-radius: 20px;
font-size: 1rem;
font-weight: 700;
font-family: var(--font-heading);
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.captain-badge.vacant {
background: color-mix(in srgb, var(--text) 4%, transparent);
border: 1px dashed var(--edge);
color: var(--text-muted);
}
/* --- Waiting indicator --- */
.waiting-box, .waiting-indicator {
@@ -428,3 +409,56 @@
gap: 0.75rem;
}
/* --- Modal overlay (shared by popups) --- */
.modal-backdrop {
position: fixed;
inset: 0;
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
background: color-mix(in srgb, var(--bg-deep) 70%, transparent);
backdrop-filter: blur(6px);
}
.modal-box {
position: relative;
max-width: 440px;
width: 100%;
max-height: 88vh;
overflow-y: auto;
padding: 1.75rem;
border: 1px solid var(--edge);
border-radius: var(--radius-md);
box-shadow: var(--shadow-deep);
}
.modal-box h3 {
margin-top: 0;
}
.modal-box.sheet-modal {
max-width: 520px;
text-align: left;
}
.modal-close {
position: absolute;
top: 0.6rem;
right: 0.75rem;
background: transparent;
border: none;
color: var(--text-muted);
font-size: 1.6rem;
line-height: 1;
cursor: pointer;
padding: 0.1rem 0.4rem;
border-radius: var(--radius-sm);
}
.modal-close:hover {
color: var(--text);
background: color-mix(in srgb, var(--text) 10%, transparent);
}

View File

@@ -1,8 +1,9 @@
/* --- Scene board layout --- */
.scene-view-layout {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: 2rem;
grid-template-columns: 180px 1.4fr 1fr;
gap: 1.5rem;
align-items: start;
}
@media (max-width: 1100px) {
@@ -11,6 +12,98 @@
}
}
/* --- Crew bubble column --- */
.crew-column {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.crew-objectives-btn {
width: 100%;
padding: 0.6rem 0.5rem;
background: color-mix(in srgb, var(--accent) 14%, transparent);
border: 1px solid var(--accent);
border-radius: var(--radius-md);
color: var(--text);
font-family: var(--font-heading);
font-size: 0.9rem;
cursor: pointer;
transition: var(--transition-smooth);
}
.crew-objectives-btn:hover {
background: color-mix(in srgb, var(--accent) 24%, transparent);
}
.crew-bubbles {
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.crew-bubble {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.1rem;
width: 100%;
text-align: left;
padding: 0.55rem 0.7rem;
background: var(--well);
border: 1px solid var(--edge);
border-left: 4px solid var(--pirat);
border-radius: var(--radius-md);
cursor: pointer;
transition: var(--transition-smooth);
}
.crew-bubble:hover {
background: color-mix(in srgb, var(--text) 8%, transparent);
transform: translateX(2px);
}
.crew-bubble.is-deep {
border-left-color: var(--deep);
background: color-mix(in srgb, var(--deep) 6%, transparent);
}
.crew-bubble.is-you {
border-color: var(--accent);
border-left-color: var(--accent);
}
.crew-bubble.is-ghost { opacity: 0.7; filter: grayscale(0.6); }
.crew-bubble.is-dead { opacity: 0.6; filter: grayscale(0.8); }
.crew-bubble .bubble-icon {
font-size: 1.1rem;
}
.crew-bubble .bubble-name {
font-weight: 700;
font-size: 0.9rem;
line-height: 1.15;
overflow-wrap: anywhere;
}
.crew-bubble .bubble-meta {
font-size: 0.75rem;
color: var(--text-muted);
}
/* On narrow screens the crew column becomes a horizontal strip above the board */
@media (max-width: 1100px) {
.crew-bubbles {
flex-direction: row;
flex-wrap: wrap;
}
.crew-bubble {
width: auto;
flex: 1 1 140px;
}
}
.card-header {
display: flex;
justify-content: space-between;
@@ -32,44 +125,6 @@
font-weight: 700;
}
/* Scene status banner (captain + roster) */
.scene-status-banner {
margin-bottom: 1.5rem;
padding: 1rem;
background: var(--well);
border: 1px solid var(--edge);
border-radius: var(--radius-md);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.roster-chips {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
align-items: center;
}
.roster-chips .roster-label {
font-size: 0.85rem;
color: var(--text-muted);
font-family: var(--font-heading);
margin-right: 0.25rem;
}
.roster-chips .player-chip {
font-size: 0.8rem;
padding: 0.25rem 0.6rem;
border-radius: 12px;
margin: 0;
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
/* --- Obstacles --- */
.obstacles-container {
display: flex;