:root { --bg-1: #0d0f24; --bg-2: #1c1140; --cyan: #22d3ee; --pink: #f472b6; --glass: rgba(255, 255, 255, 0.06); --glass-strong: rgba(255, 255, 255, 0.1); --glass-border: rgba(255, 255, 255, 0.14); --text: #ffffff; --muted: rgba(255, 255, 255, 0.55); } * { box-sizing: border-box; margin: 0; padding: 0; } html, body { height: 100%; } body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; background: linear-gradient(180deg, var(--bg-1) 0%, var(--bg-2) 100%); background-attachment: fixed; color: var(--text); min-height: 100dvh; display: flex; justify-content: center; overflow-x: hidden; } /* ---------- background blobs ---------- */ .bg-blob { position: fixed; border-radius: 50%; filter: blur(70px); pointer-events: none; z-index: 0; } .blob-cyan { width: 340px; height: 340px; left: -120px; top: 8%; background: var(--cyan); opacity: 0.2; } .blob-magenta { width: 380px; height: 380px; right: -140px; bottom: 4%; background: var(--pink); opacity: 0.16; } /* ---------- layout ---------- */ .app { position: relative; z-index: 1; width: 100%; max-width: 420px; min-height: 100dvh; display: flex; flex-direction: column; align-items: center; gap: 18px; padding: calc(28px + env(safe-area-inset-top)) 16px calc(28px + env(safe-area-inset-bottom)); } .title { font-size: clamp(28px, 8vw, 34px); font-weight: 800; letter-spacing: 0.5px; background: linear-gradient(90deg, var(--cyan), var(--pink)); -webkit-background-clip: text; background-clip: text; color: transparent; } /* ---------- scoreboard ---------- */ .scoreboard { display: flex; gap: 10px; width: 100%; } .chip { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px 8px; background: var(--glass); border: 1px solid var(--glass-border); border-radius: 16px; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); } .chip-label { font-size: 10px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; color: var(--muted); } .chip-value { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; } .chip-x { color: var(--cyan); } .chip-o { color: var(--pink); } .chip-value.pop { animation: score-pop 320ms ease; } @keyframes score-pop { 0% { transform: scale(1); } 40% { transform: scale(1.35); } 100% { transform: scale(1); } } /* ---------- status ---------- */ .status { height: 20px; font-size: 14px; color: rgba(255, 255, 255, 0.7); transition: opacity 150ms ease; } .status.fading { opacity: 0; } .status.thinking .status-text { animation: status-pulse 900ms ease-in-out infinite; } @keyframes status-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } /* ---------- board ---------- */ .board-wrap { position: relative; } .board { width: min(88vw, 340px); aspect-ratio: 1; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); gap: 10px; padding: 14px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 24px; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35); user-select: none; -webkit-user-select: none; } .board.shake { animation: shake 450ms ease; border-color: rgba(244, 63, 94, 0.5); box-shadow: 0 0 34px rgba(244, 63, 94, 0.3); } @keyframes shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-7px); } 40% { transform: translateX(6px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(3px); } } .board.pulse { animation: board-pulse 600ms ease; } @keyframes board-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.03); } } .cell { display: flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: background 150ms ease, border-color 150ms ease, box-shadow 150ms ease; } .cell.empty:active { background: var(--glass-strong); border-color: rgba(255, 255, 255, 0.25); } @media (hover: hover) { .cell.empty:hover { background: var(--glass-strong); border-color: rgba(255, 255, 255, 0.25); box-shadow: 0 0 18px rgba(34, 211, 238, 0.15); } } /* ---------- marks ---------- */ .mark { width: 58%; height: 58%; animation: mark-pop 150ms ease-out; } @keyframes mark-pop { 0% { transform: scale(0.3); opacity: 0; } 60% { transform: scale(1.12); opacity: 1; } 100% { transform: scale(1); opacity: 1; } } .mark-x { filter: drop-shadow(0 0 7px rgba(34, 211, 238, 0.65)); } .mark-o { filter: drop-shadow(0 0 7px rgba(244, 114, 182, 0.65)); } .mark line { stroke: var(--cyan); stroke-width: 9; stroke-linecap: round; } .mark circle { fill: none; stroke: var(--pink); stroke-width: 9; } /* ---------- win line ---------- */ .win-line { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; z-index: 5; } #win-line-el { filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.45)); } /* ---------- buttons ---------- */ .buttons { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 300px; } .btn { border: none; border-radius: 27px; height: 54px; font-family: inherit; font-size: 16px; font-weight: 700; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: transform 100ms ease, filter 150ms ease; } .btn:active { transform: scale(0.97); } .btn.primary { background: linear-gradient(90deg, var(--cyan), var(--pink)); color: #0d1025; box-shadow: 0 6px 24px rgba(34, 211, 238, 0.25); } .btn.primary:hover { filter: brightness(1.1); } .btn.ghost { height: 48px; font-size: 14px; font-weight: 500; border-radius: 24px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.18); color: rgba(255, 255, 255, 0.7); } .btn.ghost:hover { background: rgba(255, 255, 255, 0.09); } /* ---------- overlay ---------- */ .overlay { position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center; background: rgba(13, 15, 36, 0.55); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); opacity: 0; pointer-events: none; transition: opacity 200ms ease; padding: 24px; } .overlay.show { opacity: 1; pointer-events: auto; } .modal { width: 100%; max-width: 300px; display: flex; flex-direction: column; align-items: center; gap: 22px; padding: 34px 28px; background: var(--glass); border: 1px solid var(--glass-border); border-radius: 24px; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); transform: scale(0.85); transition: transform 250ms cubic-bezier(0.2, 1.4, 0.4, 1); } .overlay.show .modal { transform: scale(1); } .modal-title { font-size: 24px; font-weight: 800; } .modal .btn { width: 100%; } /* ---------- confetti ---------- */ #confetti-layer { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: 100; } .confetti { position: absolute; top: -24px; border-radius: 2px; animation-name: confetti-fall; animation-timing-function: linear; animation-fill-mode: forwards; } @keyframes confetti-fall { from { transform: translate3d(0, 0, 0) rotate(0deg); opacity: 1; } to { transform: translate3d(var(--drift), 108vh, 0) rotate(var(--spin)); opacity: 0.85; } } /* ---------- toast ---------- */ .toast { position: fixed; bottom: calc(24px + env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%) translateY(8px); z-index: 90; padding: 10px 20px; font-size: 14px; background: var(--glass-strong); border: 1px solid var(--glass-border); border-radius: 999px; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); opacity: 0; pointer-events: none; transition: opacity 200ms ease, transform 200ms ease; } .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }