/* Ferrous Solitaire replay viewer — Terminal (base16-eighties) palette, matching the Bevy desktop/Android app's ui_theme.rs tokens exactly. */ :root { --bg: #151515; --felt: #0f5232; --panel: #202020; --panel-hi: #2a2a2a; --text: #d0d0d0; --text-muted: #a0a0a0; --accent: #a54242; --accent-hi: #c25e5e; --red: #fb9fb1; --black: #151515; --success: #acc267; --warning: #ddb26f; --card-bg: #f8f5f0; --card-border: #c8b8a0; --card-w: 80px; --card-h: 112px; --gap: 12px; } * { box-sizing: border-box; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; display: flex; flex-direction: column; } header { padding: 16px 24px; border-bottom: 1px solid rgba(255, 255, 255, 0.05); display: flex; flex-direction: column; gap: 4px; } h1 { margin: 0; font-size: 20px; font-weight: 600; } .muted { color: var(--text-muted); } main { flex: 1; display: flex; flex-direction: column; gap: 16px; padding: 24px; align-items: center; } /* Board: a positioning context for both the dashed empty-pile slots and the absolutely-positioned card sprites. Width matches the 7-column grid (7*card-w + 6 inter-column gaps), height covers the top row plus a worst-case 13-card tableau fan. Cards live as siblings of the slot placeholders so they can move between piles without ever changing parent — the transform-based `transition` then animates the flight. */ #board { position: relative; background: var(--felt); border-radius: 12px; padding: 24px; width: calc(7 * var(--card-w) + 6 * var(--gap)); /* Top row + a generous fan budget (12 fan steps + the card's own height) so a king-down-to-ace column never overflows. */ height: calc(var(--card-h) + 32px + var(--card-h) + 12 * 28px); } /* Empty-pile slot placeholders are absolutely positioned at the same coordinates the renderer uses for cards, so they line up perfectly when the pile is empty. */ .slot { position: absolute; width: var(--card-w); height: var(--card-h); border: 2px dashed rgba(255, 255, 255, 0.15); border-radius: 8px; } .card { position: absolute; /* `top: 0; left: 0` plus a per-card `transform: translate(...)` gives us a single transformed property to animate. Using `transform` (rather than `top` / `left`) lets the browser run the animation on the compositor — smooth even on the low-spec laptops the player tests on. */ top: 0; left: 0; width: var(--card-w); height: var(--card-h); background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 6px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); padding: 4px 6px; font-family: "Helvetica Neue", Arial, sans-serif; font-weight: 600; line-height: 1; user-select: none; transition: transform 280ms cubic-bezier(0.22, 1, 0.36, 1), opacity 200ms ease; will-change: transform; } .card.face-down { background: repeating-linear-gradient( 45deg, #2a2a2a 0, #2a2a2a 6px, #202020 6px, #202020 12px ); color: transparent; border-color: #353535; } .card .corner { position: absolute; font-size: 14px; line-height: 1.1; text-align: center; } .card .corner.top { top: 4px; left: 6px; } .card .corner.bottom { bottom: 4px; right: 6px; transform: rotate(180deg); } .card.red { color: var(--red); } .card.black { color: var(--black); } .card .center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 28px; } #controls { display: flex; gap: 12px; align-items: center; } #controls button { background: var(--panel-hi); color: var(--text); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 6px; padding: 8px 16px; cursor: pointer; font-size: 14px; font-family: inherit; } #controls button:hover:not(:disabled) { background: var(--accent-hi); color: var(--text); } #controls button:disabled { opacity: 0.5; cursor: default; } #status { display: flex; gap: 24px; font-size: 14px; } #status #result.win { color: var(--success); font-weight: 600; }