$gate-node: 64px; $gate-gap: 36px; $gate-line: 2px; .room-page { position: relative; display: flex; align-items: center; justify-content: center; flex: 1; min-height: 0; overflow: hidden; } #id_room_menu { position: fixed; bottom: 6.6rem; right: 0.5rem; z-index: 314; background-color: rgba(var(--priUser), 0.95); border: 0.15rem solid rgba(var(--secUser), 1); box-shadow: 0 0 0.5rem rgba(var(--secUser), 0.75), 0.12rem 0.12rem 0.5rem rgba(0, 0, 0, 0.25) ; border-radius: 0.75rem; padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem; } // Scroll-lock when gate is open. Uses html (not body) to avoid CSS overflow // propagation quirk on Linux headless Firefox where body overflow:hidden can // 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-backdrop) { overflow: hidden; } // Aperture fill — solid --duoUser layer that covers the game table (.room-page). // Uses position:absolute so it's clipped to .room-page bounds (overflow:hidden), // naturally staying below the h2 title + navbar/footer in both orientations. // Sits at z-90: below blur backdrops (z-100) which render on top via backdrop-filter. // Fades in/out via opacity transition when a backdrop class is present. #id_aperture_fill { position: absolute; inset: 0; background: rgba(var(--duoUser), 1); z-index: 90; pointer-events: none; opacity: 0; transition: opacity 0.15s ease; } html:has(.gate-backdrop) #id_aperture_fill, html:has(.sig-backdrop) #id_aperture_fill, html:has(.role-select-backdrop) #id_aperture_fill { opacity: 1; } .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; z-index: 120; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; pointer-events: none; margin-top: 5rem; } .gate-modal { display: flex; flex-direction: column; align-items: stretch; gap: 0.5rem; min-width: 26rem; pointer-events: auto; border: none; background-color: transparent; .gate-title-panel { border: 0.1rem solid rgba(var(--terUser), 0.25); border-radius: 0.5rem; padding: 0.75rem; background: rgba(var(--priUser), 1); } .gate-top-row { display: flex; flex-direction: row; gap: 0.5rem; } .gate-main-panel { flex: 3; min-width: 0; display: flex; flex-direction: column; align-items: center; border: 0.1rem solid rgba(var(--terUser), 0.25); border-radius: 0.5rem; padding: 0.75rem; background: rgba(var(--priUser), 1); } .gate-roles-panel { flex: 1; min-width: 5rem; display: flex; align-items: center; justify-content: center; border: 0.1rem solid rgba(var(--terUser), 0.25); border-radius: 0.5rem; padding: 0.75rem; background: rgba(var(--priUser), 1); .launch-game-btn { margin-top: 0; } } .gate-invite-panel { display: flex; flex-direction: column; gap: 0.4rem; border: 0.1rem solid rgba(var(--terUser), 0.25); border-radius: 0.5rem; padding: 0.75rem; background: rgba(var(--priUser), 1); } .gate-header { text-align: center; h1 { font-size: 2rem; color: rgba(var(--secUser), 0.6); margin-bottom: 1rem; text-align: justify; text-align-last: center; text-justify: inter-character; text-transform: uppercase; text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.125), // highlight (up-left) var(--title-shadow-offset) var(--title-shadow-offset) 0 rgba(0, 0, 0, 0.8) // shadow (down-right) ; span { color: rgba(var(--quaUser), 0.6); } margin: 0 0 0.5rem; } .gate-status-wrap { display: flex; justify-content: center; align-items: baseline; opacity: 0.5; font-size: 0.75em; text-transform: uppercase; letter-spacing: 0.15em; .status-dots { display: inline-flex; span { display: inline-block; width: 0.5em; text-align: center; } } } } .token-slot { position: relative; display: flex; flex-direction: row; border: 2px solid rgba(var(--terUser), 0.7); border-radius: 0.4rem; background: rgba(0, 0, 0, 0.35); min-width: 180px; &.locked { opacity: 0.3; pointer-events: none; } &.ready { border-color: rgba(var(--terUser), 1); button.token-rails { box-shadow: 0 0 0.6rem rgba(var(--terUser), 0.6), 0 0 1.6rem rgba(var(--terUser), 0.25) ; .rail { background: rgba(var(--terUser), 1); } } } &.pending, &.claimed { box-shadow: 0 0 0.6rem rgba(var(--terUser), 0.5), 0 0 1.4rem rgba(var(--terUser), 0.2), ; .token-return-btn { text-shadow: 0 0 0.5rem rgba(var(--terUser), 0.8); } &:hover { border-color: rgba(var(--terUser), 1); background: rgba(0, 0, 0, 0.55); box-shadow: 0 0 0.8rem rgba(var(--terUser), 0.75), 0 0 2rem rgba(var(--terUser), 0.35), ; } } .token-rails, button.token-rails { display: flex; flex-direction: row; align-items: stretch; padding: 0.6rem 0.45rem; gap: 0.2rem; border-right: 1px solid rgba(var(--terUser), 0.35); .rail { display: block; width: 2px; background: rgba(var(--terUser), 0.55); border-radius: 1px; } } button.token-rails { background: transparent; border: none; outline: none; border-right: 1px solid rgba(var(--terUser), 0.35); cursor: pointer; border-radius: 0.3rem 0 0 0.3rem; &:hover { background: rgba(var(--terUser), 0.1); .rail { background: rgba(var(--terUser), 1); } } } .token-return-btn { position: absolute; inset: 0; background: transparent; border: none; outline: none; cursor: pointer; border-radius: inherit; } .token-panel { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 0.45rem 0.75rem; gap: 0.15rem; .token-denomination { font-size: 1.5em; font-weight: bold; color: rgba(var(--terUser), 1); line-height: 1; } .token-insert-label, .token-insert-btn { &::before { content: '← '; } font-size: 0.6em; text-transform: uppercase; letter-spacing: 0.08em; text-align: center; line-height: 1.3; } .token-return-label { font-size: 0.55em; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.5; line-height: 1.3; text-align: center; } } } } // Narrow viewport — scale down, 2×3 slot grid (portrait mobile + narrow desktop) @media (max-width: 700px) { // Floor the gatekeeper modal below the position-strip circles (~1.5rem top + 3rem height) .gate-overlay { padding-top: 5.5rem; } .gate-modal { padding: 1.25rem 1.5rem; .gate-header { h1 { font-size: 1.5rem; } } .token-slot { min-width: 150px; } } } // ─── Room shell layout ───────────────────────────────────────────────────── .room-shell { display: flex; flex-direction: row; align-items: stretch; gap: 2rem; width: 100%; max-height: 80vh; align-self: stretch; } // ─── Table hex + seat positions ──────────────────────────────────────────── // // .table-hex: regular pointy-top hexagon. // clip-path polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%) // on a 160×185 container gives equal-length sides (height = width × 2/√3). // // Seats use absolute positioning from the .room-table centre. // $seat-r = 130px — radius to seat centroid // $seat-r-x = round(130px × sin60°) = 113px — horizontal component // $seat-r-y = round(130px × cos60°) = 65px — vertical component // // Clockwise from top: slots 1→2→3→4→5→6. $seat-r: 130px; $seat-r-x: round($seat-r * 0.866); // 113px $seat-r-y: round($seat-r * 0.5); // 65px // Seat edge-midpoint geometry (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 // ─── Position strip ──────────────────────────────────────────────────────── // Numbered gate-slot circles sit above the gate backdrop/overlay (z 130 > 120 // > 100) but below the role-select fan (z 200), tray (310), and menus (310+). // .room-page is position:relative with no z-index, so its absolute children // share the root stacking context with the fixed overlays. // When the gate modal or role-select fan is open, suppress pointer events so // the strip doesn't intercept clicks or hover effects on the modal beneath it // (landscape: strip overlaps centered card fan too). // Must target .gate-slot directly — it has an explicit pointer-events: auto // override that wins over a rule on the parent .position-strip alone. html:has(.gate-backdrop) .position-strip .gate-slot, html:has(.role-select-backdrop) .position-strip .gate-slot { pointer-events: none; } // Re-enable clicks on confirm/reject/drop-token forms inside slots html:has(.gate-backdrop) .position-strip .gate-slot form, html:has(.gate-backdrop) .position-strip .gate-slot button { pointer-events: auto; } .position-strip { position: absolute; top: 1rem; left: 0; right: 0; z-index: 130; display: flex; justify-content: center; gap: round($gate-gap * 0.6); pointer-events: none; .gate-slot { position: relative; width: round($gate-node * 0.75); height: round($gate-node * 0.75); border-radius: 50%; border: $gate-line solid rgba(var(--terUser), 0.5); background: rgba(var(--priUser), 1); display: flex; flex-direction: column; align-items: center; justify-content: center; flex-shrink: 0; pointer-events: auto; font-size: 1.8rem; transition: opacity 0.6s ease, transform 0.6s ease; box-shadow: 0.1rem 0.1rem 0.12rem rgba(var(--priUser), 0.25), 0.12rem 0.12rem 0.25rem rgba(0, 0, 0, 0.25), 0.25rem 0.25rem 0.25rem rgba(var(--priUser), 0.12) ; &.role-assigned { opacity: 0; transform: scale(0.5); pointer-events: none; box-shadow: 0.1rem 0.1rem 0.12rem rgba(var(--terUser), 0.25), 0.12rem 0.12rem 0.25rem rgba(0, 0, 0, 0.25), 0.25rem 0.25rem 0.25rem rgba(var(--terUser), 0.12) ; } &.filled, &.reserved { background: rgba(var(--terUser), 0.9); border-color: rgba(var(--terUser), 1); color: rgba(var(--priUser), 1); } &.filled:hover, &.reserved:hover { box-shadow: -0.1rem -0.1rem 1rem rgba(var(--ninUser), 1), -0.1rem -0.1rem 0.25rem rgba(0, 0, 0, 1), 0.05rem 0.05rem 0.5rem rgba(0, 0, 0, 1); } .slot-number { font-size: 0.7em; opacity: 0.5; } .slot-gamer { display: none; } form { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; } &:has(.drop-token-btn) { background: rgba(var(--terUser), 1); border-color: rgba(var(--ninUser), 0.5); &:hover { box-shadow: -0.1rem -0.1rem 1rem rgba(var(--ninUser), 1), -0.1rem -0.1rem 0.25rem rgba(0, 0, 0, 1), 0.05rem 0.05rem 0.5rem rgba(0, 0, 0, 1); } } } } @media (max-width: 700px) { .position-strip { gap: round($gate-gap * 0.3); .gate-slot { width: round($gate-node * 0.75); height: round($gate-node * 0.75); } } } .room-table { flex: 2; position: relative; display: flex; align-items: center; justify-content: center; min-height: 0; } // Fixed design-size scene; JS scales it to fill .room-table via transform: scale(). // Design dims: seat reach is ±110px H / ±95px V from centre + seat element size. .room-table-scene { width: 360px; height: 300px; position: relative; display: flex; align-items: center; justify-content: center; transform-origin: center center; } // Hex border: clip-path clips CSS borders, so the ring is a wrapper with the // same hex polygon at a slightly larger size. 0.25rem each side — subtle only. .table-hex-border { width: calc(160px + 0.5rem); height: calc(185px + 0.5rem); clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); background: rgba(var(--quaUser), 1); filter: drop-shadow(0 0 6px rgba(var(--quaUser), 0.5)); display: flex; align-items: center; justify-content: center; } .table-hex { width: 160px; height: 185px; clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); // Six gradients — one per hex face — each perpendicular to that face so the // shadows follow the hex geometry rather than the rectangular bounding box. // CSS angle convention: 0°=up, 90°=right. Shadow goes FROM face INWARD. // Left face → 90° Right face → 270° // Top-left face → 150° Top-right face → 210° // Bottom-left face → 30° Bottom-right face→ 330° background: linear-gradient(90deg, rgba(0, 0, 0, 0.2) 0%, transparent 15%), linear-gradient(90deg, rgba(var(--quaUser), 0.1) 0%, transparent 15%), linear-gradient(270deg, rgba(0, 0, 0, 0.2) 0%, transparent 15%), linear-gradient(270deg, rgba(var(--quaUser), 0.1) 0%, transparent 15%), linear-gradient(210deg, rgba(0, 0, 0, 0.2) 0%, transparent 15%), linear-gradient(210deg, rgba(var(--quaUser), 0.1) 0%, transparent 15%), linear-gradient(150deg, rgba(0, 0, 0, 0.2) 0%, transparent 15%), linear-gradient(150deg, rgba(var(--quaUser), 0.1) 0%, transparent 15%), linear-gradient(30deg, rgba(0, 0, 0, 0.2) 0%, transparent 15%), linear-gradient(30deg, rgba(var(--quaUser), 0.1) 0%, transparent 15%), linear-gradient(330deg, rgba(0, 0, 0, 0.2) 0%, transparent 15%), linear-gradient(330deg, rgba(var(--quaUser), 0.1) 0%, transparent 15%), rgba(var(--duoUser), 1); display: flex; align-items: center; justify-content: center; } // Outside .room-table-scene so it isn't scaled by scaleTable(). // Positioned absolute so it floats over the hex without affecting flex layout. #id_pick_sigs_wrap { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; } .table-center { display: flex; align-items: center; justify-content: center; } .table-seat { position: absolute; display: grid; grid-template-columns: auto auto; grid-template-rows: auto auto; column-gap: 0.25rem; align-items: center; transform: translate(-50%, -50%); pointer-events: none; // 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}); } // Chair: col 1, spans both rows .fa-chair { grid-column: 1; grid-row: 1 / 3; font-size: 1.6rem; color: rgba(var(--secUser), 0.4); transition: color 0.6s ease, filter 0.6s ease; } // Abbreviation: col 2, row 1 .seat-role-label { grid-column: 2; grid-row: 1; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.05em; color: rgba(var(--secUser), 1); } // Status icon: col 2, row 2, centred under the abbreviation .position-status-icon { grid-column: 2; grid-row: 2; justify-self: center; font-size: 0.8rem; &.fa-ban { color: rgba(var(--priRd), 1); } &.fa-circle-check { color: rgba(var(--priGn), 1); } } // Left-side positions: flip column order so chair is closest to the table &[data-slot="3"], &[data-slot="4"], &[data-slot="5"] { .fa-chair { grid-column: 2; } .seat-role-label { grid-column: 1; } .position-status-icon { grid-column: 1; } } &.active .fa-chair { color: rgba(var(--terUser), 1); filter: drop-shadow(0 0 4px rgba(var(--ninUser), 1)); } // After role confirmed: chair settles to full-opacity --secUser (no glow) &.role-confirmed .fa-chair { color: rgba(var(--secUser), 1); filter: none; } .seat-portrait { width: 36px; height: 36px; border-radius: 50%; border: 2px solid rgba(var(--terUser), 1); display: flex; align-items: center; justify-content: center; font-size: 0.75rem; opacity: 0.6; } .seat-label { font-size: 0.65rem; opacity: 0.5; max-width: 80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } // Arc of mini cards — visible only on the currently active seat .seat-card-arc { display: none; position: absolute; width: 18px; height: 26px; border-radius: 2px; border: 1px solid rgba(var(--terUser), 0.7); background: rgba(var(--quaUser), 0.9); // Three fanned cards stacked behind the portrait &::before, &::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: inherit; background: inherit; } &::before { transform: rotate(-18deg) translate(-4px, 2px); } &::after { transform: rotate( 18deg) translate( 4px, 2px); } } &.active .seat-portrait { opacity: 1; border-color: rgba(var(--secUser), 1); box-shadow: 0 0 0.5rem rgba(var(--ninUser), 0.5); } &.active .seat-card-arc { display: block; transform: translateY(-28px); // float above the portrait } } // ─── Card stack ──────────────────────────────────────────────────────────── .card-stack { width: 90px; height: 60px; display: flex; align-items: center; justify-content: center; border-radius: 6px; border: 2px solid rgba(var(--quiUser), 1); background: rgba(var(--terUser), 1); cursor: default; transition: box-shadow 0.2s ease; position: relative; &::before { content: "ROLE"; font-size: 0.6rem; letter-spacing: 0.14em; color: rgba(var(--quiUser), 1); } .fa-ban { position: absolute; font-size: 1.4rem; } &[data-state="eligible"] { cursor: pointer; border: 2px solid rgba(var(--quiUser), 1); box-shadow: 0 0 0.6rem rgba(var(--ninUser), 1), 0 0 1.6rem rgba(var(--secUser), 0.25); } &[data-state="ineligible"] { opacity: 0.4; cursor: not-allowed; } } // ─── Card dimensions ─────────────────────────────────────────────────────── // Base size matches the card-stack footprint; --table-scale (set by scaleTable() // in room.js) stretches both the grid and individual cards to stay in sync with // the scene transform. Fallback of 1 keeps the fan functional if JS hasn't run. $card-w: 90px; $card-h: 60px; // ─── Role select modal ───────────────────────────────────────────────────── .role-select-backdrop { position: fixed; inset: 0; z-index: 200; display: flex; justify-content: center; align-items: center; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(4px); cursor: pointer; } #id_role_select { // Always a 3×2 grid — 6 landscape cards in a row would overflow any viewport. display: grid; grid-template-columns: repeat(3, calc(#{$card-w} * var(--table-scale, 1))); gap: 1rem; pointer-events: none; } // ─── Card component ──────────────────────────────────────────────────────── .card { width: calc(#{$card-w} * var(--table-scale, 1)); height: calc(#{$card-h} * var(--table-scale, 1)); border-radius: 6px; cursor: pointer; pointer-events: auto; position: relative; perspective: 600px; .card-back, .card-front { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; border-radius: inherit; border: 2px solid rgba(var(--terUser), 1); background: rgba(var(--quiUser), 1); backface-visibility: hidden; -webkit-backface-visibility: hidden; transition: transform 0.35s ease; } .card-back { transform: rotateY(0deg); font-size: calc(0.66rem * var(--table-scale, 1)); letter-spacing: 0.14em; color: rgba(var(--quiUser), 1); background: rgba(var(--terUser), 1); border: 2px solid rgba(var(--quiUser), 1); } .card-front { transform: rotateY(180deg); padding: 0.5rem; text-align: center; .card-role-name { font-size: calc(0.66rem * var(--table-scale, 1)); color: rgba(var(--quaUser), 1); text-transform: uppercase; letter-spacing: 0.05em; } } &.flipped, &.face-up { .card-back { transform: rotateY(-180deg); } .card-front { transform: rotateY(0deg); } } } // Landscape mobile — aggressively scale down to fit short viewport @media (orientation: landscape) { // Sink navbar below gate/role-select overlays when a modal is open. // Landscape navbar z-index is 100 (_base.scss); gate-backdrop/overlay are // 100/120 — same level causes paint-order ties so we drop it to 50. html:has(.gate-backdrop) body .container .navbar, html:has(.role-select-backdrop) body .container .navbar { z-index: 50; } // Position strip: horizontal row across the top, slots 1-6 in order. // Offset from both sidebars (5rem each) and centred with gap. .position-strip { flex-direction: row; top: 2.5rem; left: 5rem; right: 5rem; justify-content: center; gap: round($gate-gap * 0.4); } // Small landscape (phones ≤550px tall): strip stays horizontal — no two-column // trick needed now that the h2 is in the gutter. Just clear any order overrides. @media (max-height: 550px) { .position-strip { .gate-slot { order: 0; } top: 1rem; } } } // ─── Sig Select overlay (SIG_SELECT phase) ──────────────────────────────────── // // Two overlays (levity / gravity) run in parallel, one per polarity group. // Layout mirrors the gatekeeper: dark Gaussian backdrop + centred modal. // Inside the modal: upper stage (card preview) + lower mini card grid (no scroll). html:has(.sig-backdrop) { overflow: hidden; } .sig-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.75); backdrop-filter: blur(5px); z-index: 100; pointer-events: none; } .sig-overlay { position: fixed; inset: 0; display: flex; align-items: stretch; justify-content: center; z-index: 120; pointer-events: none; } .sig-modal { pointer-events: auto; display: flex; flex-direction: column; width: 100%; // respects overlay padding-right set by JS max-width: 420px; max-height: 100%; // respects overlay padding-bottom set by JS } // ─── Stage ──────────────────────────────────────────────────────────────────── // flex: 1 — fills all space above the card grid; no background (backdrop blur). // Row layout: preview card bottom-left, stat block fills the right. // Card width is set by sizeSigCard() in room.js (smaller of 40% stage width or // 80% stage height × 5/8) via --sig-card-w CSS variable — libsass can't handle // container query units inside min(). .sig-stage { flex: 1; min-height: 0; position: relative; display: flex; flex-direction: row; align-items: flex-end; padding-left: 1.5rem; gap: 0.75rem; // Preview card — width driven by JS via --sig-card-w; aspect-ratio derives height. .sig-stage-card { flex-shrink: 0; width: var(--sig-card-w, 120px); height: auto; aspect-ratio: 5 / 8; border-radius: 0.5rem; background: rgba(var(--priUser), 1); border: 0.15rem solid rgba(var(--secUser), 0.6); display: flex; flex-direction: column; position: relative; padding: 0.25rem; overflow: hidden; // game-kit sets .fan-card-corner { position: absolute; top/left offsets } // so these just need display/font overrides; the corners land at the card edges. // All font-sizes scale with --sig-card-w (ratio = original-rem × 16 / 120). .fan-card-corner--tl { display: flex; flex-direction: column; align-items: center; line-height: 1.1; gap: 0.1rem; .fan-corner-rank { font-size: calc(var(--sig-card-w, 120px) * 0.133); font-weight: 700; } i { font-size: calc(var(--sig-card-w, 120px) * 0.1); } } .fan-card-corner--br { display: flex; flex-direction: column; align-items: center; line-height: 1.1; gap: 0.1rem; .fan-corner-rank { font-size: calc(var(--sig-card-w, 120px) * 0.12); font-weight: 700; } i { font-size: calc(var(--sig-card-w, 120px) * 0.1); } } .fan-card-face { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 0.25rem 0.15rem; gap: 0.2rem; .fan-card-name-group { font-size: calc(var(--sig-card-w, 120px) * 0.073); opacity: 0.6; } .fan-card-name { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; } .fan-card-arcana { font-size: calc(var(--sig-card-w, 120px) * 0.067); text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.5; } .fan-card-correspondence{ font-size: calc(var(--sig-card-w, 120px) * 0.067); opacity: 0.5; } } } // Stat block — same dimensions as the preview card (width × 5:8 aspect). // flex: 0 0 auto so it doesn't stretch to fill the stage; the rest of the // stage row is simply empty, giving the card room to breathe. .sig-stat-block { flex: 0 0 auto; width: var(--sig-card-w, 120px); height: calc(var(--sig-card-w, 120px) * 8 / 5); align-self: flex-end; background: rgba(var(--priUser), 0.5); border-radius: 0.4rem; border: 0.1rem solid rgba(var(--terUser), 0.15); display: none; position: relative; .sig-flip-btn { position: absolute; top: -1rem; right: -1rem; margin: 0; z-index: 50; } .sig-caution-btn { position: absolute; top: 1.25rem; right: -1rem; margin: 0; z-index: 50; } // Caution tooltip — covers the entire stat block (inset: 0), z-index above buttons. .sig-caution-tooltip { display: none; position: absolute; inset: 0; z-index: 60; background-color: rgba(var(--tooltip-bg), 0.6); backdrop-filter: blur(6px); border-radius: 0.4rem; border: 0.1rem solid rgba(var(--priYl), 0.35); padding: 0.75rem; flex-direction: column; gap: 0.4rem; overflow-y: auto; } .sig-caution-header { display: flex; flex-direction: column; gap: 0.1rem; } .sig-caution-title { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 700; margin: 0; color: rgba(var(--priYl), 1); } .sig-caution-type { font-size: calc(var(--sig-card-w, 120px) * 0.058); opacity: 0.7; text-transform: uppercase; letter-spacing: 0.05em; flex-shrink: 0; } .sig-caution-shoptalk { font-size: calc(var(--sig-card-w, 120px) * 0.063); opacity: 0.55; margin: 0; font-style: italic; } .sig-caution-effect { flex: 1; font-size: calc(var(--sig-card-w, 120px) * 0.075); margin: 0; line-height: 1.55; .card-ref { color: rgba(var(--terUser), 1); font-weight: 600; } } .sig-caution-index { font-size: calc(var(--sig-card-w, 120px) * 0.063); opacity: 0.55; } // Nav arrows portaled out of tooltip — sit at bottom corners above tooltip (z-70) .sig-caution-prev, .sig-caution-next { display: none; position: absolute; bottom: -1rem; margin: 0; z-index: 70; } .sig-caution-prev { left: -1rem; } .sig-caution-next { right: -1rem; } .stat-face { display: none; padding: calc(var(--sig-card-w, 120px) * 0.37) calc(var(--sig-card-w, 120px) * 0.1) calc(var(--sig-card-w, 120px) * 0.08); &--upright { display: block; } } &.is-reversed { .stat-face--upright { display: none; } .stat-face--reversed { display: block; } } .stat-face-label { font-size: calc(var(--sig-card-w, 120px) * 0.063); text-transform: uppercase; letter-spacing: 0.09em; opacity: 0.4; margin: 0 0 calc(var(--sig-card-w, 120px) * 0.07); } .stat-keywords { list-style: none; padding: 0; margin: 0; li { font-size: calc(var(--sig-card-w, 120px) * 0.083); padding: calc(var(--sig-card-w, 120px) * 0.042) 0; opacity: 0.85; border-bottom: 0.05rem solid rgba(var(--terUser), 0.12); &:last-child { border-bottom: none; } } } } &.sig-stage--frozen .sig-stat-block { display: block; } &.sig-caution-open .sig-stat-block { .sig-caution-tooltip { display: flex; } .sig-caution-prev, .sig-caution-next { display: inline-flex; } } } // ─── Mini card grid ─────────────────────────────────────────────────────────── // flex: 0 0 auto — shrinks to card content; no background (backdrop blur). // align-content: start prevents CSS grid from distributing extra height between rows. .sig-deck-grid { flex: 0 0 auto; display: grid; grid-template-columns: repeat(6, 1fr); align-content: start; gap: 2px; padding: 4px; overflow: hidden; margin: 0 1rem 5rem 4rem; } .sig-card { aspect-ratio: 5 / 8; border-radius: 0.4rem; background: rgba(var(--priUser), 0.97); border: 1px solid rgba(var(--secUser), 0.3); position: relative; cursor: grab; transition: border-color 0.15s, box-shadow 0.15s; overflow: hidden; // game-kit sets .fan-card-corner { position:absolute; top:0.4rem; left:0.4rem } // Override: center the element within the card instead. .fan-card-corner--tl { top: 50%; left: 50%; transform: translate(-50%, -50%); gap: 0; // game-kit has gap:0.15rem — too large at 0.5rem font-size .fan-corner-rank { font-size: 1rem; font-weight: 700; } i { font-size: 0.75rem; } } // OK / NVM overlay — appears on click (focused) or own reservation .sig-card-actions { position: absolute; inset: 0; display: none; flex-direction: column; align-items: center; justify-content: center; gap: 3px; background: rgba(var(--priUser), 0.92); border-radius: inherit; .sig-nvm-btn { display: none; } } &.sig-focused .sig-card-actions { display: flex; } &.sig-reserved--own .sig-card-actions { display: flex; .sig-ok-btn { display: none; } .sig-nvm-btn { display: flex; } } // Cursor anchors strip — bottom of card .sig-card-cursors { position: absolute; bottom: 2px; left: 2px; right: 2px; display: flex; justify-content: space-between; } &:hover:not([data-reserved-by]) { border-color: rgba(var(--secUser), 0.8); box-shadow: 0 0 4px rgba(var(--secUser), 0.25); } &.sig-reserved { border-color: rgba(var(--terUser), 1); box-shadow: 0 0 0.4rem rgba(var(--terUser), 0.7), 0 0 1rem rgba(var(--ninUser), 0.4); cursor: not-allowed; } &.sig-reserved--own { border-color: rgba(var(--secUser), 1); box-shadow: 0 0 0.4rem rgba(var(--secUser), 0.7), 0 0 1rem rgba(var(--ninUser), 0.5); cursor: grabbing; } } // ─── Cursor anchors ─────────────────────────────────────────────────────────── // // Three tiny dots along the bottom of each mini card, one per role in the group. // Inactive: invisible. Active (another gamer is hovering): coloured dot. .sig-cursor { display: block; width: 5px; height: 5px; border-radius: 50%; background: transparent; transition: background 0.1s; &.active { background: rgba(var(--terUser), 1); box-shadow: 0 0 3px rgba(var(--ninUser), 0.8); } } // ─── Sig select: landscape overrides ───────────────────────────────────────── // Landscape base: 9×2 grid of 3rem cards. At ≥992px (wide enough for 18 cards // at 3rem + 4rem left + ~4rem right): collapse to a single 18×1 row so the // stage preview gets maximum vertical real-estate. // padding-left clears the fixed left navbar (JS sets right/bottom but not left). // Grid margins reset to 0 — overlay padding handles all edge clearance. @media (orientation: landscape) { .sig-modal { max-width: none; flex-direction: row; // grid to the right, stage + card preview to the left margin-left: 4rem; margin-right: 3rem; } .sig-stage { min-width: 0; // allow shrinking in row layout; align-items:flex-end already set } .sig-deck-grid { grid-template-columns: repeat(6, 2.5rem); margin: 0; align-self: flex-end; // sit at the bottom of the modal row } } @media (orientation: landscape) and (min-width: 900px) { // Wide landscape: revert to stacked layout (stage top, 18-card row grid bottom). .sig-modal { flex-direction: column; align-items: stretch; } .sig-stage { min-width: auto; align-self: stretch; // fill full modal width so JS sizeSigCard() gets correct stageWidth margin-left: 3rem; } .sig-deck-grid { grid-template-columns: repeat(18, 3rem); align-self: center; } } @media (orientation: landscape) and (min-width: 1800px) { // Sig overlay: clear doubled sidebars (8rem each instead of 4rem/6rem) .sig-overlay { padding-left: 8rem; padding-right: 8rem; } .sig-stage { align-self: stretch; // fill full modal width so JS sizeSigCard() gets correct stageWidth margin-left: 3rem; } .sig-deck-grid { grid-template-columns: repeat(18, 5rem); align-self: center; } // Room menu: base right: 0.5rem (same-specificity ID rule) overrides _applets.scss // XL block because _room.scss is imported later. Re-declare here to win the cascade. #id_room_menu { right: 2.5rem; } } // ─── Seat tray — see _tray.scss ─────────────────────────────────────────────