hex position indicators: chair icons at hex edge midpoints replace gate-slot circles

- Split .gate-overlay into .gate-backdrop (z-100, blur) + .gate-overlay modal (z-120) so .table-position elements (z-110) render above backdrop but below modal
- New _table_positions.html partial: 6 .table-position divs with .fa-chair, role label, and .fa-ban/.fa-circle-check status icons; included unconditionally in room.html
- New epic:room view at /gameboard/room/<uuid>/; gatekeeper redirects there when table_status set; pick_roles redirects there
- role-select.js: adds .active glow to position on selectRole(); swaps .fa-ban→.fa-circle-check in placeCard onComplete; handleTurnChanged clears stale .active from all positions
- FTs: PositionIndicatorsTest (5 tests) + RoleSelectTest 8a/8b (glow + check state)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-03-30 18:31:05 -04:00
parent 8b006be138
commit a8592aeaec
11 changed files with 370 additions and 35 deletions

View File

@@ -36,25 +36,29 @@ $gate-line: 2px;
// disrupt pointer events on position:fixed descendants.
// NOTE: may be superfluous — root cause of CI kit-btn failures turned out to be
// game-kit.js missing from git (was in gitignored STATIC_ROOT only).
html:has(.gate-overlay) {
html:has(.gate-backdrop) {
overflow: hidden;
}
.gate-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
z-index: 100;
pointer-events: none;
}
.gate-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
z-index: 100;
z-index: 120;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
// Prevents backdrop from intercepting clicks on position:fixed elements
// (e.g. #id_kit_btn) in Linux headless Firefox.
// NOTE: may be superfluous — see html:has comment above.
pointer-events: none;
}
@@ -362,6 +366,63 @@ $seat-r: 130px;
$seat-r-x: round($seat-r * 0.866); // 113px
$seat-r-y: round($seat-r * 0.5); // 65px
// .table-position anchors at edge midpoints (pointy-top hex).
// Apothem ≈ 80px + 30px clearance = 110px total push from centre.
$pos-d: 110px;
$pos-d-x: round($pos-d * 0.5); // 55px
$pos-d-y: round($pos-d * 0.866); // 95px
.table-position {
position: absolute;
z-index: 110;
pointer-events: none;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
// Edge midpoints, clockwise from 3 o'clock (slot drop order → role order)
&[data-slot="1"] { left: calc(50% + #{$pos-d}); top: 50%; }
&[data-slot="2"] { left: calc(50% + #{$pos-d-x}); top: calc(50% + #{$pos-d-y}); }
&[data-slot="3"] { left: calc(50% - #{$pos-d-x}); top: calc(50% + #{$pos-d-y}); }
&[data-slot="4"] { left: calc(50% - #{$pos-d}); top: 50%; }
&[data-slot="5"] { left: calc(50% - #{$pos-d-x}); top: calc(50% - #{$pos-d-y}); }
&[data-slot="6"] { left: calc(50% + #{$pos-d-x}); top: calc(50% - #{$pos-d-y}); }
.position-body {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.1rem;
}
.fa-chair {
font-size: 1.1rem;
color: rgba(var(--secUser), 0.4);
}
.position-role-label {
font-size: 0.6rem;
font-weight: 600;
letter-spacing: 0.05em;
color: rgba(var(--secUser), 0.5);
}
.position-status-icon {
font-size: 0.65rem;
&.fa-ban { color: rgba(var(--priRd), 1); }
&.fa-circle-check { color: rgba(var(--priGn), 1); }
}
&.active {
.fa-chair {
color: rgba(var(--terUser), 1);
filter: drop-shadow(0 0 4px rgba(var(--ninUser), 1));
}
}
}
.room-table {
flex: 2;
position: relative;