Files
python-tdd/src/static_src/scss/_card-deck.scss
2026-04-29 11:49:26 -04:00

1278 lines
42 KiB
SCSS
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.

// ─── Card deck primitives — fan cards + sig-select overlay ─────────────────────
//
// Shared card display classes (.fan-card, .fan-card-corner, .fan-card-face, .fan-nav)
// extracted from _game-kit.scss; sig-select overlay extracted from _room.scss.
// ── Tarot fan modal ──────────────────────────────────────────────────────────
#id_tarot_fan_dialog {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
max-width: none;
max-height: none;
margin: 0;
padding: 0;
border: none;
background: rgba(0, 0, 0, 0.88);
overflow: hidden;
&::backdrop { display: none; } // Dialog IS the backdrop
}
.tarot-fan-wrap {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
perspective: 900px;
button {
box-shadow: none;
&:hover, &.active {
box-shadow: none;
}
}
}
.tarot-fan {
position: relative;
width: 220px;
height: 340px;
}
.fan-card {
position: absolute;
inset: 0;
width: 220px;
height: 340px;
border-radius: 0.75rem;
background: rgba(var(--priUser), 1);
border: 0.1rem solid rgba(var(--secUser), 0.4);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.25s ease, opacity 0.25s ease;
transform-style: preserve-3d;
&--active {
border-color: rgba(var(--secUser), 1);
box-shadow: 0 0 2rem rgba(var(--secUser), 0.3);
}
}
.fan-card-corner {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
line-height: 1;
color: rgba(var(--secUser), 0.75);
&--tl { top: 0.4rem; left: 0.4rem; }
&--br { bottom: 0.4rem; right: 0.4rem; transform: rotate(180deg); }
.fan-corner-rank {
font-size: 1.5rem;
font-weight: bold;
padding: 0.18rem 0;
}
i { font-size: 1.5rem; }
}
.fan-card-face {
padding: 1.25rem;
text-align: center;
display: flex;
flex-direction: column;
gap: 0.5rem;
.fan-card-number { font-size: 0.65rem; }
.fan-card-name-group { font-size: 0.65rem; margin: 0; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(var(--secUser), 1); }
.fan-card-name { font-size: 0.95rem; font-weight: bold; margin: 0; color: rgba(var(--terUser), 1); }
.fan-card-arcana { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.1em; color: rgba(var(--secUser), 1); }
.fan-card-correspondence { font-size: 0.6rem; font-style: italic; color: rgba(var(--secUser), 0.5); }
}
.fan-nav {
position: absolute;
z-index: 20;
font-size: 3rem;
line-height: 1;
background: none;
border: none;
color: rgba(var(--secUser), 0.6);
cursor: pointer;
padding: 1rem;
transition: color 0.15s;
pointer-events: auto;
&:hover { color: rgba(var(--secUser), 1); }
// Suppress browser focus ring on mouse/touch clicks; retain it for keyboard nav
&:focus:not(:focus-visible) { outline: none; box-shadow: none; }
&--prev { left: 1rem; }
&--next { right: 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;
transition: transform 0.4s ease;
// 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-face-upright { display: flex; flex-direction: column; align-items: center; gap: 0.15rem; }
.fan-card-face-reversal { display: flex; flex-direction: column; align-items: center; gap: 0.15rem; padding-top: 0.1rem; }
.fan-card-name-group { font-size: calc(var(--sig-card-w, 120px) * 0.073); opacity: 0.6; }
// Upright qualifier + name share sizing/weight/color with their reversed counterparts
.sig-qualifier-above,
.sig-qualifier-below,
.fan-card-reversal-qualifier { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; color: rgba(var(--quiUser), 1); transition: opacity 0.2s; }
.fan-card-name,
.fan-card-reversal-name { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; color: rgba(var(--quiUser), 1); transition: opacity 0.2s; }
.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{ display: none; } // Minchiate equivalence shown in game-kit only
// Reversed face elements — pre-rotated so they read forward after card spins
.fan-card-reversal-qualifier,
.fan-card-reversal-name {
transform: rotate(180deg);
opacity: 0.25;
}
}
&.stage-card--reversed {
transform: rotate(180deg);
.fan-card-reversal-qualifier,
.fan-card-reversal-name { opacity: 1; }
.fan-card-name,
.sig-qualifier-above,
.sig-qualifier-below { opacity: 0.25; }
}
}
// 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-info-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-info {
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-info-header {
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.sig-info-title {
font-size: calc(var(--sig-card-w, 120px) * 0.093);
font-weight: 700;
margin: 0;
&--energies { color: rgba(var(--quaUser), 1); }
&--operations { color: rgba(var(--quaUser), 1); }
}
.sig-info-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-info-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-info-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-info-prev,
.sig-info-next {
display: none;
position: absolute;
bottom: -1rem;
margin: 0;
z-index: 70;
}
.sig-info-prev { left: -1rem; }
.sig-info-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-info-open .sig-stat-block {
.sig-info { display: flex; }
.sig-info-prev, .sig-info-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 strip — hangs below the card bottom edge; overflow: visible allows this.
.sig-card-cursors {
position: absolute;
bottom: -0.6rem;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
padding: 0 2px;
}
// Rise above DOM-order siblings when a peer's cursor is active on this card.
// Without this, later cards in the grid paint over the overflowing cursor icons.
&:has(.sig-cursor.active) { z-index: 5; }
&:hover:not([data-reserved-by]) {
border-color: rgba(var(--secUser), 0.8);
box-shadow: 0 0 4px rgba(var(--secUser), 0.25);
}
&.sig-reserved {
cursor: not-allowed;
}
// Role-coloured reservation glow — border/shadow matches the reserving gamer's role.
// data-reserved-by is set by applyReservation() in sig-select.js.
// Own reservation also shows role colour (same as peers see), not a separate style.
&.sig-reserved {
&[data-reserved-by="PC"] { border-color: rgba(var(--priRd), 1); box-shadow: 0 0 0 2px rgba(var(--priRd), 1); }
&[data-reserved-by="NC"] { border-color: rgba(var(--priYl), 1); box-shadow: 0 0 0 2px rgba(var(--priYl), 1); }
&[data-reserved-by="EC"] { border-color: rgba(var(--priGn), 1); box-shadow: 0 0 0 2px rgba(var(--priGn), 1); }
&[data-reserved-by="SC"] { border-color: rgba(var(--priCy), 1); box-shadow: 0 0 0 2px rgba(var(--priCy), 1); }
&[data-reserved-by="AC"] { border-color: rgba(var(--priId), 1); box-shadow: 0 0 0 2px rgba(var(--priId), 1); }
&[data-reserved-by="BC"] { border-color: rgba(var(--priFs), 1); box-shadow: 0 0 0 2px rgba(var(--priFs), 1); }
}
&.sig-reserved--own {
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): role-coloured dot.
// Position order is fixed per polarity (POLARITY_ROLES in sig-select.js):
// levity (PC / NC / SC) → left / mid / right
// gravity (BC / EC / AC) → left / mid / right
// In-card cursor elements — invisible anchors only.
// Visible icons are portaled to document root by applyHover() in sig-select.js.
.sig-cursor {
display: block;
font-size: 0; // zero-size: no layout impact, just carries .active class
color: transparent;
pointer-events: none;
}
// ─── Floating cursor portal ───────────────────────────────────────────────────
//
// sig-select.js creates these <i> elements inside #id_sig_cursor_portal, a
// position:fixed root-level container, so they escape all overflow/clip contexts.
// Positioned via getBoundingClientRect() on the card element.
#id_sig_cursor_portal {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 200; // above sig-overlay (120), below tray (310)
overflow: visible;
}
.sig-cursor-float {
position: absolute;
font-size: 1.5rem;
line-height: 1;
transform: translateX(-50%); // centre on the x coordinate from JS
pointer-events: none;
}
// Role-specific colour + outline shadow + ninUser glow
.sig-cursor-float[data-role="PC"] {
color: rgba(var(--priRd), 1);
text-shadow: 2px 0 0 rgba(var(--priOr),1), -2px 0 0 rgba(var(--priOr),1),
0 2px 0 rgba(var(--priOr),1), 0 -2px 0 rgba(var(--priOr),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="NC"] {
color: rgba(var(--priYl), 1);
text-shadow: 2px 0 0 rgba(var(--priLm),1), -2px 0 0 rgba(var(--priLm),1),
0 2px 0 rgba(var(--priLm),1), 0 -2px 0 rgba(var(--priLm),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="EC"] {
color: rgba(var(--priGn), 1);
text-shadow: 2px 0 0 rgba(var(--priTk),1), -2px 0 0 rgba(var(--priTk),1),
0 2px 0 rgba(var(--priTk),1), 0 -2px 0 rgba(var(--priTk),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="SC"] {
color: rgba(var(--priCy), 1);
text-shadow: 2px 0 0 rgba(var(--priBl),1), -2px 0 0 rgba(var(--priBl),1),
0 2px 0 rgba(var(--priBl),1), 0 -2px 0 rgba(var(--priBl),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="AC"] {
color: rgba(var(--priId), 1);
text-shadow: 2px 0 0 rgba(var(--priVt),1), -2px 0 0 rgba(var(--priVt),1),
0 2px 0 rgba(var(--priVt),1), 0 -2px 0 rgba(var(--priVt),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
.sig-cursor-float[data-role="BC"] {
color: rgba(var(--priFs), 1);
text-shadow: 2px 0 0 rgba(var(--priMe),1), -2px 0 0 rgba(var(--priMe),1),
0 2px 0 rgba(var(--priMe),1), 0 -2px 0 rgba(var(--priMe),1),
0 0 6px rgba(0, 0, 0, 0.5);
}
// ─── Polarity theming — card colour inversion ────────────────────────────────
//
// Gravity (Graven): --priUser bg / --secUser text — standard dark palette.
// Levity (Leavened): --secUser bg / --priUser text — inverted, lighter feel.
// Both mini-cards and the stage preview card follow the same rule.
.sig-overlay[data-polarity="levity"] {
// Mini card: inverted palette. game-kit sets explicit colours on .fan-card-name
// and .fan-card-corner that out-specifc the parent color, so re-target them here.
.sig-card {
background: rgba(var(--secUser), 0.97);
border-color: rgba(var(--priUser), 0.3);
color: rgba(var(--priUser), 1);
.fan-card-corner { color: rgba(var(--priUser), 0.75); }
.fan-card-name { color: rgba(var(--quiUser), 1); }
// OK / NVM overlay — must match the inverted card background
.sig-card-actions { background: rgba(var(--secUser), 0.92); }
}
// Stage preview card: same inversion + title colour.
// .fan-card-name-group and .fan-card-arcana have explicit color in the base
// .fan-card-face rule (specificity 0,2,0) — must re-target them here (0,3,0).
// Opacity dim is still applied by the nested sig-stage-card rule.
.sig-stage-card {
background: rgba(var(--secUser), 1);
border-color: rgba(var(--priUser), 0.6);
color: rgba(var(--priUser), 1);
.fan-card-corner { color: rgba(var(--priUser), 0.75); }
.fan-card-name-group{ color: rgba(var(--priUser), 1); }
.fan-card-name { color: rgba(var(--quiUser), 1); }
.fan-card-arcana { color: rgba(var(--priUser), 1); }
}
// Polarity qualifier: same colour as the card title in this context
.sig-qualifier-above,
.sig-qualifier-below { color: rgba(var(--quiUser), 1); }
// Upright + reversal title glow — levity
.sig-stage-card .fan-card-name,
.sig-stage-card .sig-qualifier-above,
.sig-stage-card .sig-qualifier-below,
.sig-stage-card .fan-card-reversal-name,
.sig-stage-card .fan-card-reversal-qualifier {
text-shadow: 0 1px 1px rgba(0,0,0,0.55), 0 0 0.55rem rgba(var(--ninUser), 0.7);
}
// card-ref spans inside the caution tooltip — must match the base rule's
// .sig-stat-block .sig-info-effect .card-ref specificity (0,3,0) to win.
.sig-info-effect .card-ref { color: rgba(var(--quiUser), 1); }
// Cursor colours live in .sig-cursor-float[data-role] rules (portal elements)
}
.sig-overlay[data-polarity="gravity"] {
// Stat block: invert priUser/secUser so gravity gets the same stark contrast as leavened cards
.sig-stat-block {
background: rgba(var(--secUser), 0.75);
color: rgba(var(--priUser), 1);
border-color: rgba(var(--priUser), 0.15);
}
// Caution tooltip: --tooltip-bg is black so priUser text (dark) would be invisible —
// override to secUser (light) so body text reads against the dark backdrop.
.sig-info { color: rgba(var(--secUser), 1); }
// Polarity qualifier: terUser for gravity (quiUser is levity's equivalent)
.sig-qualifier-above,
.sig-qualifier-below { color: rgba(var(--terUser), 1); }
// Upright + reversal title glow — gravity
.sig-stage-card .fan-card-name,
.sig-stage-card .sig-qualifier-above,
.sig-stage-card .sig-qualifier-below,
.sig-stage-card .fan-card-reversal-name,
.sig-stage-card .fan-card-reversal-qualifier {
text-shadow: 1px 1px 0 rgba(0,0,0,1), 0 0 0.25rem rgba(var(--ninUser), 0.25);
}
// Cursor colours live in .sig-cursor-float[data-role] rules (portal elements)
}
// ─── 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 _card-deck.scss is imported after _applets.scss. Re-declare here to win the cascade.
#id_room_menu { right: 2.5rem; }
}
// ── PICK SEA overlay ─────────────────────────────────────────────────────────
// Mirrors .natus-* structure but with columns reversed:
// left = transparent (Celtic Cross card positions)
// right = rgba(--priUser) opaque (spread select)
.sea-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(4px);
z-index: 200;
}
html.sea-open .sea-backdrop { display: block; }
.sea-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 201;
overflow-y: auto;
align-items: center;
justify-content: center;
}
html.sea-open .sea-overlay { display: flex; }
.sea-modal-wrap {
position: relative;
width: 90vw;
max-width: 60rem;
max-height: 90vh;
margin: auto;
opacity: 0;
transform: translateY(1.5rem);
transition: opacity 0.25s, transform 0.25s;
}
html.sea-open .sea-modal-wrap {
opacity: 1;
transform: translateY(0);
}
.sea-modal {
border-radius: 0.5rem;
overflow: hidden;
width: 100%;
}
.sea-modal-header {
padding: 0.75rem 1.25rem;
background: rgba(var(--priUser), 1);
h2 { font-size: 1.4rem; margin: 0; }
p { margin: 0.2rem 0 0; font-size: 0.85rem; opacity: 0.8; }
}
.sea-modal-body {
display: flex;
min-height: 20rem;
}
// ── Cards column (transparent / left) ────────────────────────────────────────
.sea-cards-col {
flex: 1 1 55%;
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem;
}
.sea-cross {
display: grid;
grid-template-areas:
". crown . "
"past center future "
". root . ";
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto auto auto;
gap: 0.5rem;
align-items: center;
justify-items: center;
}
.sea-cross-cell { display: flex; align-items: center; justify-content: center; }
.sea-pos-crown { grid-area: crown; }
.sea-pos-past { grid-area: past; }
.sea-pos-center { grid-area: center; }
.sea-pos-future { grid-area: future; }
.sea-pos-root { grid-area: root; }
.sea-pos-crossing { grid-area: crossing; }
$sea-card-w: 4rem;
$sea-card-h: 6.5rem;
.sea-card-slot {
width: $sea-card-w;
height: $sea-card-h;
background-color: rgba(var(--duoUser), 1);
border: 0.15rem dashed rgba(var(--terUser), 1);
box-shadow: 0 0 2px rgba(var(--priUser), 0.5);
border-radius: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
color: rgba(var(--terUser), 0.6);
}
.sea-card-slot--crossing {
// Keep portrait dimensions; rotate(90deg) in .sea-pos-cross supplies the landscape visual.
// Swapping w/h here caused flex-shrink to squish the longer edge to fit the 4rem container.
width: $sea-card-w;
height: $sea-card-h;
}
.sea-card-slot--filled {
// Start invisible; transition to .sea-card-slot--visible on deposit
opacity: 0;
transition: opacity 1s ease;
border: 0.15rem solid transparent;
border-radius: 0.3rem;
flex-direction: column;
gap: 0.15rem;
.fan-corner-rank { font-size: 1.15rem; font-weight: 700; line-height: 1; }
i { font-size: 0.9rem; }
}
// Levity drawn card — secUser bg, priUser text + border (matches stage card polarity)
.sea-card-slot--filled.sea-card-slot--levity {
color: rgba(var(--priUser), 0.9);
background: rgba(var(--secUser), 1);
border-color: rgba(var(--priUser), 1);
}
// Gravity drawn card — priUser bg, secUser text + border
.sea-card-slot--filled.sea-card-slot--gravity {
color: rgba(var(--secUser), 0.9);
background: rgba(var(--priUser), 1);
border-color: rgba(var(--secUser), 0.6);
}
// Deposited — fully opaque by default; Cover/Cross are semi-transparent
.sea-card-slot--visible { opacity: 1; transition: opacity 1s ease, box-shadow 0.15s ease; }
@keyframes sea-cover-appear {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0.3; }
}
@keyframes sea-cross-appear {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0.15; }
}
.sea-pos-cover .sea-card-slot--visible { opacity: 0.3; animation: sea-cover-appear 2s ease; }
.sea-pos-cross .sea-card-slot--visible { opacity: 0.15; animation: sea-cross-appear 2s ease; }
// Hover: reveal fully (snappy)
.sea-pos-cover .sea-card-slot--visible:hover,
.sea-pos-cross .sea-card-slot--visible:hover { opacity: 1; transition: opacity 0.15s ease; }
// Focused (first tap): persist at opacity 1 + selection glow until modal opens
.sea-card-slot--focused {
opacity: 1 !important;
transition: opacity 0.15s ease, box-shadow 0.15s ease;
box-shadow: 0 0 0.5rem 0.25rem rgba(var(--ninUser), 0.35), 0 0 0.4rem rgba(0, 0, 0, 0.85);
}
// Cover + Cross — absolutely overlaid on the Sig card in .sea-pos-center
.sea-pos-center { position: relative; }
.sea-pos-cover,
.sea-pos-cross {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
.sea-card-slot { pointer-events: auto; }
}
.sea-pos-cover { z-index: 3; } // above sig (z-index: 2)
.sea-pos-cross { z-index: 4; } // above cover
// Empty Cover/Cross slots are invisible — they reveal only once a card is deposited
.sea-pos-cover .sea-card-slot--empty,
.sea-pos-cross .sea-card-slot--empty { opacity: 0; pointer-events: none; }
.sea-pos-cross .sea-card-slot { transform: rotate(90deg); }
// Sig card in center slot — compact rank + icon display; tilted CCW so Cover slot peeks through
.sea-sig-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.2rem;
transform: rotate(-5deg);
position: relative;
z-index: 2;
.fan-corner-rank {
font-size: 1.2rem;
font-weight: 700;
line-height: 1;
color: rgba(var(--secUser), 0.85);
}
i { font-size: 1rem; color: rgba(var(--secUser), 0.75); }
}
// .sig-stage-card is normally scoped inside .sig-stage — re-apply the card shell
// here so it renders correctly outside that context.
.sea-cross .sig-stage-card {
flex-shrink: 0;
width: var(--sig-card-w, #{$sea-card-w});
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;
.fan-card-face {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 0.25rem 0.15rem;
.fan-card-name,
.sig-qualifier-above,
.sig-qualifier-below { font-size: 0.5rem; font-weight: 600; white-space: normal; word-break: break-word; line-height: 1.3; margin: 0; }
}
}
// ── Form column (priUser / opaque / right) ────────────────────────────────────
.sea-form-col {
flex: 0 0 auto;
width: 16rem;
display: flex;
flex-direction: column;
padding: 1.25rem;
background: rgba(var(--priUser), 1);
}
.sea-form-main {
flex: 1;
overflow-y: auto;
}
.sea-field {
display: flex;
flex-direction: column;
gap: 0.35rem;
margin-bottom: 1rem;
label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.7; }
}
.sea-select {
background: rgba(var(--duoUser), 0.6);
border: 1px solid rgba(var(--terUser), 0.3);
border-radius: 0.3rem;
color: inherit;
padding: 0.4rem 0.5rem;
font-size: 0.85rem;
width: 100%;
option { background: rgba(var(--priUser), 1); }
}
// Deck stacks — DECKS label + gravity + levity piles
.sea-stacks {
display: flex;
align-items: center;
gap: 0.75rem;
margin: 1rem 0;
}
.sea-stacks-label {
writing-mode: vertical-rl;
transform: rotate(180deg);
text-transform: uppercase;
// Fill the full card height ($sea-card-h: 6.5rem) with 5 letters
font-size: 1rem;
letter-spacing: 0.32em;
font-weight: 700;
opacity: 0.5;
white-space: nowrap;
flex-shrink: 0;
align-self: center;
}
.sea-deck-stack {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
cursor: pointer;
}
.sea-stack-face {
position: relative;
width: $sea-card-w;
height: $sea-card-h;
border-radius: 0.3rem;
border: 0.15rem solid;
display: flex;
align-items: center;
justify-content: center;
transition: box-shadow 0.15s;
z-index: 1; // sits above the name label
}
.sea-stack-ok {
position: absolute;
top: 50%;
left: 0;
right: 0;
margin: 0 auto;
transform: translateY(-50%);
z-index: 5;
}
.sea-deck-stack { gap: 0; } // remove gap so name slides under the face
.sea-stack-name {
font-size: 0.65rem;
letter-spacing: 0.08em;
text-transform: uppercase;
font-weight: 600;
opacity: 0.6;
// Pull top of label partially under the stack face
// margin-top: -0.1rem;
transform: scaleY(1.2);
transform-origin: top center;
z-index: 0;
}
.sea-deck-stack--gravity .sea-stack-name { color: rgba(var(--quaUser), 1); }
.sea-deck-stack--levity .sea-stack-name { color: rgba(var(--terUser), 1); }
// Deck backs — face-down pile colour identifies polarity
$_sea-shadow: 1px 2px 0 rgba(0,0,0,0.7), 0 4px 0 rgba(0,0,0,0.18), 2px 5px 5px rgba(0,0,0,0.5);
$_glow-levity: 0 0 0.8rem 0.15rem rgba(var(--ninUser), 0.6);
$_glow-gravity: 0 0 0.8rem 0.15rem rgba(var(--quaUser), 0.6);
.sea-deck-stack--levity .sea-stack-face {
background: rgba(var(--terUser), 0.88);
border-color: rgba(var(--ninUser), 0.65);
box-shadow: $_sea-shadow;
}
.sea-deck-stack--gravity .sea-stack-face {
background: rgba(var(--quiUser), 0.88);
border-color: rgba(var(--quaUser), 0.65);
box-shadow: $_sea-shadow;
}
// Glow on hover, :active, and while OK is showing (--active class set by JS)
.sea-deck-stack--levity:hover .sea-stack-face,
.sea-deck-stack--levity:active .sea-stack-face,
.sea-deck-stack--levity.sea-deck-stack--active .sea-stack-face { box-shadow: $_sea-shadow, $_glow-levity; }
.sea-deck-stack--gravity:hover .sea-stack-face,
.sea-deck-stack--gravity:active .sea-stack-face,
.sea-deck-stack--gravity.sea-deck-stack--active .sea-stack-face { box-shadow: $_sea-shadow, $_glow-gravity; }
// Form action row — LOCK HAND + DEL side by side at the bottom
.sea-form-actions {
display: flex;
gap: 0.5rem;
margin-top: auto;
padding-top: 0.75rem;
}
// NVM button — same positioning as .natus-modal-wrap > .btn-cancel
.sea-modal-wrap > .btn-cancel {
position: absolute;
top: -1rem;
right: -1rem;
z-index: 10;
}
// ── Sea stage — big card viewer ───────────────────────────────────────────────
.sea-stage {
position: fixed;
inset: 0;
z-index: 200;
display: flex;
align-items: center;
justify-content: center;
}
.sea-stage-backdrop {
position: absolute;
inset: 0;
cursor: pointer;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(4px);
}
.sea-stage-content {
position: relative;
z-index: 1;
display: flex;
flex-direction: row;
align-items: flex-end;
gap: 0.75rem;
padding: 1.5rem;
}
// Stage card — size matches sig-select stage (--sig-card-w driven by inline style)
.sea-stage-card {
flex-shrink: 0;
width: var(--sig-card-w, 140px);
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;
transform-style: preserve-3d;
transition: transform 0.4s ease;
// Flip-in animation when stage opens
&--shown {
animation: sea-flip-in 0.35s ease forwards;
}
.fan-card-corner--tl,
.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, 140px) * 0.133); font-weight: 700; }
i { font-size: calc(var(--sig-card-w, 140px) * 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-face-upright { display: flex; flex-direction: column; align-items: center; gap: 0.15rem; }
.fan-card-face-reversal { display: flex; flex-direction: column; align-items: center; gap: 0.15rem; padding-top: 0.1rem; }
.fan-card-name-group { font-size: calc(var(--sig-card-w, 140px) * 0.073); opacity: 0.6; }
.sig-qualifier-above,
.sig-qualifier-below,
.fan-card-reversal-qualifier { font-size: calc(var(--sig-card-w, 140px) * 0.093); font-weight: 600; color: rgba(var(--quiUser), 1); transition: opacity 0.2s; }
.fan-card-name,
.fan-card-reversal-name { font-size: calc(var(--sig-card-w, 140px) * 0.093); font-weight: 600; color: rgba(var(--quiUser), 1); transition: opacity 0.2s; }
.fan-card-arcana { font-size: calc(var(--sig-card-w, 140px) * 0.067); text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.5; }
.fan-card-reversal-qualifier,
.fan-card-reversal-name { transform: rotate(180deg); opacity: 0.25; }
}
&.stage-card--reversed {
transform: rotate(180deg);
.fan-card-reversal-qualifier,
.fan-card-reversal-name { opacity: 1; }
.fan-card-name,
.sig-qualifier-above,
.sig-qualifier-below { opacity: 0.25; }
}
}
@keyframes sea-flip-in {
0% { transform: perspective(600px) rotateY(-90deg) scale(0.4); opacity: 0; }
60% { transform: perspective(600px) rotateY(8deg) scale(1.03); opacity: 1; }
100% { transform: perspective(600px) rotateY(0deg) scale(1); opacity: 1; }
}
// Sea stage card title — polarity-specific colour + shared glow
$_sea-title-shadow: 1px 1px 0 rgba(0,0,0,1), 0 0 0.25rem rgba(var(--ninUser), 0.25);
$_sea-title-els: '.fan-card-name, .sig-qualifier-above, .sig-qualifier-below, .fan-card-reversal-name, .fan-card-reversal-qualifier';
.sea-stage--levity .sea-stage-card {
background: rgba(var(--secUser), 1);
border-color: rgba(var(--priUser), 1);
color: rgba(var(--priUser), 1);
.fan-card-arcana,
.fan-card-corner {
color: rgba(var(--priUser), 1);
}
.fan-card-name, .sig-qualifier-above, .sig-qualifier-below,
.fan-card-reversal-name, .fan-card-reversal-qualifier {
color: rgba(var(--quiUser), 1);
text-shadow: $_sea-title-shadow;
}
}
.sea-stage--gravity .sea-stage-card {
.fan-card-name, .sig-qualifier-above, .sig-qualifier-below,
.fan-card-reversal-name, .fan-card-reversal-qualifier {
color: rgba(var(--terUser), 1);
text-shadow: $_sea-title-shadow;
}
}
// Sea stat block — reuses sig-select stat-block sizing, scoped to sea-stage
.sea-stage-content .sea-stat-block {
flex: 0 0 auto;
width: var(--sig-card-w, 140px);
height: calc(var(--sig-card-w, 140px) * 8 / 5);
background: rgba(var(--priUser), 0.85);
border-radius: 0.4rem;
border: 0.1rem solid rgba(var(--terUser), 0.15);
position: relative;
display: block;
.sea-spin-btn { position: absolute; top: -1rem; right: -1rem; margin: 0; z-index: 50; }
.sea-fyi-btn { position: absolute; top: 1.25rem; right: -1rem; margin: 0; z-index: 50; }
.stat-face { display: none; padding: calc(var(--sig-card-w, 140px) * 0.37) calc(var(--sig-card-w, 140px) * 0.1) calc(var(--sig-card-w, 140px) * 0.08); }
.stat-face--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, 140px) * 0.063); text-transform: uppercase; letter-spacing: 0.09em; opacity: 0.4; margin: 0 0 calc(var(--sig-card-w, 140px) * 0.07); }
.stat-keywords { list-style: none; padding: 0; margin: 0;
li { font-size: calc(var(--sig-card-w, 140px) * 0.083); padding: calc(var(--sig-card-w, 140px) * 0.042) 0; opacity: 0.85; border-bottom: 0.05rem solid rgba(var(--terUser), 0.12); &:last-child { border-bottom: none; } }
}
.sig-info {
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;
display: none;
}
.sea-fyi-prev,
.sea-fyi-next { display: inline-flex; position: absolute; bottom: -1rem; margin: 0; z-index: 70; }
.sea-fyi-prev { left: -1rem; }
.sea-fyi-next { right: -1rem; }
}
@media (orientation: landscape) {
html.sea-open body .container .navbar,
html.sea-open body #id_footer {
z-index: 90;
}
}